READ ME

SUMMARY
This R code is used to estimate MO2 and Pcrit (commonly understood as the threshold below which oxygen consumption rate can no longer be sustained) in the Common galaxias (Galaxias maculatus). The associated article is “The role of osmorespiratory compromise in hypoxia tolerance of the purportedly oxyconforming teleost Galaxias maculatus

AUTHORS
To be added

AFFILIATIONS
To be added

AIM
To be added

Knit settings

These are the settings for the html output. We will use this to make out index file on Git

#kniter seetting
knitr::opts_chunk$set(
message = FALSE,
warning = FALSE, # no warnings
cache = TRUE,# Cacheing to save time when kniting
tidy = TRUE
)

Required packages

These are the R packages required for this script. You will need to install a package called pacman to run the p_load function.

# this installs and load packages
# need to install pacman
pacman::p_load("ggplot2", 
               "ggthemes", 
               "ggfortify", 
               "gtExtras", 
               "igraph",
               "dagitty",
               "ggdag",
               "ggridges",
               "gghalves",
               "ggExtra",
               "gridExtra",
               "corrplot",
               "RColorBrewer", 
               "gt", 
               "gtsummary",
               "grid",
               "plotly", # data visualisation
               
                "tidyverse", 
               "janitor", 
               "readxl", 
               "broom", 
               "data.table", 
               "devtools",
               "hms", # data tidy
               
               "marginaleffects", 
               "brms", 
               "rstan", 
               "performance", 
               "emmeans", 
               "tidybayes", 
               "vegan",
               "betareg",
               "lme4", 
               "car", 
               "lmerTest",
               "qqplotr",
               "respirometry",
               "mclust",
               # modelling 
              
               
               "datawizard", 
               "SRS" # data manipulation 
                       )

Functions (custom)

Here are some custom function used within this script.

calcSMR: authored by Chabot D. used to estimate SMR with several different methods

calcSMR = function(Y, q = c(0.1, 0.15, 0.2, 0.25, 0.3), G = 1:4) {
    u = sort(Y)
    the.Mclust <- Mclust(Y, G = G)
    cl <- the.Mclust$classification
    # sometimes, the class containing SMR is not called 1 the following
    # presumes that when class 1 contains > 10% of cases, it contains SMR,
    # otherwise we take class 2
    cl2 <- as.data.frame(table(cl))
    cl2$cl <- as.numeric(levels(cl2$cl))
    valid <- cl2$Freq >= 0.1 * length(time)
    the.cl <- min(cl2$cl[valid])
    left.distr <- Y[the.Mclust$classification == the.cl]
    mlnd = the.Mclust$parameters$mean[the.cl]
    CVmlnd = sd(left.distr)/mlnd * 100
    quant = quantile(Y, q)
    low10 = mean(u[1:10])
    low10pc = mean(u[6:(5 + round(0.1 * (length(u) - 5)))])
    # remove 5 outliers, keep lowest 10% of the rest, average Herrmann & Enders
    # 2000
    return(list(mlnd = mlnd, quant = quant, low10 = low10, low10pc = low10pc, cl = cl,
        CVmlnd = CVmlnd))
}

calcO2crit: authored by Chabot D. used to estimate O2crit (Pcript)

calcO2crit <- function(Data, SMR, lowestMO2 = NA, gapLimit = 4, max.nb.MO2.for.reg = 20) {
    # AUTHOR: Denis Chabot, Institut Maurice-Lamontagne, DFO, Canada first
    # version written in June 2009 last updated in January 2015
    method = "LS_reg"  # will become 'through_origin' if intercept is > 0
    if (is.na(lowestMO2))
        lowestMO2 = quantile(Data$MO2[Data$DO >= 80], p = 0.05)
    # Step 1: identify points where MO2 is proportional to DO
    geqSMR = Data$MO2 >= lowestMO2
    pivotDO = min(Data$DO[geqSMR])
    lethal = Data$DO < pivotDO
    N_under_SMR = sum(lethal)  # points available for regression?
    final_N_under_SMR = lethal  # some points may be removed at Step 4
    lastMO2reg = Data$MO2[Data$DO == pivotDO]  # last MO2 when regulating
    if (N_under_SMR > 1)
        theMod = lm(MO2 ~ DO, data = Data[lethal, ])
    # Step 2, add one or more point at or above SMR 2A, when there are fewer
    # than 3 valid points to calculate a regression
    if (N_under_SMR < 3) {
        missing = 3 - sum(lethal)
        not.lethal = Data$DO[geqSMR]
        DOlimit = max(sort(not.lethal)[1:missing])  # highest DO acceptable
        # to reach a N of 3
        addedPoints = Data$DO <= DOlimit
        lethal = lethal | addedPoints
        theMod = lm(MO2 ~ DO, data = Data[lethal, ])
    }
    # 2B, add pivotDO to the fit when Step 1 yielded 3 or more values?
    if (N_under_SMR >= 3) {
        lethalB = Data$DO <= pivotDO  # has one more value than 'lethal'
        regA = theMod
        regB = lm(MO2 ~ DO, data = Data[lethalB, ])
        large_slope_drop = (coef(regA)[2]/coef(regB)[2]) > 1.1  # arbitrary
        large_DO_gap = (max(Data$DO[lethalB]) - max(Data$DO[lethal])) > gapLimit
        tooSmallMO2 = lastMO2reg < SMR
        if (!large_slope_drop & !large_DO_gap & !tooSmallMO2)
            {
                lethal = lethalB
                theMod = regB
            }  # otherwise we do not accept the additional point
    }
    # Step 3 if the user wants to limit the number of points in the regression
    if (!is.na(max.nb.MO2.for.reg) & sum(lethal) > max.nb.MO2.for.reg) {
        Ranks = rank(Data$DO)
        lethal = Ranks <= max.nb.MO2.for.reg
        theMod = lm(MO2 ~ DO, data = Data[lethal, ])
        final_N_under_SMR = max.nb.MO2.for.reg
    }
    # Step 4
    predMO2 = as.numeric(predict(theMod, data.frame(DO = Data$DO)))
    Data$delta = (Data$MO2 - predMO2)/predMO2 * 100  # residuals set to zero
    # when below pivotDO
    Data$delta[Data$DO < pivotDO | lethal] = 0
    tol = 0  # any positive residual is unacceptable
    HighValues = Data$delta > tol
    Ranks = rank(-1 * Data$delta)
    HighMO2 = HighValues & Ranks == min(Ranks)  # keep largest residual
    if (sum(HighValues) > 0)
        {
            nblethal = sum(lethal)
            Data$W = NA
            Data$W[lethal] = 1/nblethal
            Data$W[HighMO2] = 1
            theMod = lm(MO2 ~ DO, weight = W, data = Data[lethal | HighMO2, ])
            # This new regression is always an improvement, but there can still
            # be points above the line, so we repeat
            predMO2_2 = as.numeric(predict(theMod, data.frame(DO = Data$DO)))
            Data$delta2 = (Data$MO2 - predMO2_2)/predMO2_2 * 100
            Data$delta2[Data$DO < pivotDO] = 0
            tol = Data$delta2[HighMO2]
            HighValues2 = Data$delta2 > tol
            if (sum(HighValues2) > 0)
                {
                  Ranks2 = rank(-1 * Data$delta2)
                  HighMO2_2 = HighValues2 & Ranks2 == 1  # keep the largest residual
                  nblethal = sum(lethal)
                  Data$W = NA
                  Data$W[lethal] = 1/nblethal
                  Data$W[HighMO2_2] = 1
                  theMod2 = lm(MO2 ~ DO, weight = W, data = Data[lethal | HighMO2_2,
                    ])
                  # is new slope steeper than the old one?
                  if (theMod2$coef[2] > theMod$coef[2]) {
                    theMod = theMod2
                    HighMO2 = HighMO2_2
                  }
                }  # end second search for high value
        }  # end first search for high value
    Coef = coefficients(theMod)
    # Step 5, check for positive intercept
    AboveOrigin = Coef[1] > 0
    # if it is, we use a regression that goes through the origin
    if (AboveOrigin) {
        theMod = lm(MO2 ~ DO - 1, data = Data[lethal, ])
        Coef = c(0, coefficients(theMod))  # need to add the intercept (0)
        # manually to have a pair of coefficients
        method = "through_origin"
        HighMO2 = rep(FALSE, nrow(Data))  # did not use the additional value
        # from Step 4
    }
    po2crit = as.numeric(round((SMR - Coef[1])/Coef[2], 1))
    sum_mod = summary(theMod)
    anov_mod = anova(theMod)
    O2CRIT = list(o2crit = po2crit, SMR = SMR, Nb_MO2_conforming = N_under_SMR, Nb_MO2_conf_used = final_N_under_SMR,
        High_MO2_required = sum(HighMO2) == 1, origData = Data, Method = method,
        mod = theMod, r2 = sum_mod$r.squared, P = anov_mod$"Pr(>F)", lethalPoints = which(lethal),
        AddedPoints = which(HighMO2))
}  # end function

plotO2crit: used to plot the modes used for the calcO2crit function

plotO2crit <- function(o2critobj, plotID = "", Xlab = "Dissolved oxygen (% sat.)",
    Ylab = "dotitalumol", smr.cex = 0.9, o2crit.cex = 0.9, plotID.cex = 1.2, Transparency = T,
    ...) {
    # AUTHOR: Denis Chabot, Institut Maurice-Lamontagne, DFO, Canada first
    # version written in June 2009 last updated 2015-02-09 for R plotting
    # devices that do not support transparency (e.g., postscript), set
    # Transparency to FALSE
    smr = o2critobj$SMR
    if (Ylab %in% c("dotitalumol", "italumol", "dotumol", "umol", "dotitalmg", "italmg",
        "dotmg", "mg")) {
        switch(Ylab, dotitalumol = {
            mo2.lab = expression(paste(italic(dot(M))[O[2]], " (", mu, "mol ", O[2],
                " ", min^-1, " ", kg^-1, ")"))
        }, italumol = {
            mo2.lab = expression(paste(italic(M)[O[2]], " (", mu, "mol ", O[2], " ",
                min^-1, " ", kg^-1, ")"))
        }, dotumol = {
            mo2.lab = expression(paste(dot(M)[O[2]], " (", mu, "mol ", O[2], " ",
                min^-1, " ", kg^-1, ")"))
        }, umol = {
            mo2.lab = expression(paste(M[O[2]], " (", mu, "mol ", O[2], " ", min^-1,
                " ", kg^-1, ")"))
        }, dotitalmg = {
            mo2.lab = expression(paste(italic(dot(M))[O[2]], " (mg ", O[2], " ",
                h^-1, " ", kg^-1, ")"))
        }, italmg = {
            mo2.lab = expression(paste(italic(M)[O[2]], " (mg ", O[2], " ", h^-1,
                " ", kg^-1, ")"))
        }, dotmg = {
            mo2.lab = expression(paste(dot(M)[O[2]], " (mg ", O[2], " ", h^-1, " ",
                kg^-1, ")"))
        }, mg = {
            mo2.lab = expression(paste(M[O[2]], " (mg ", O[2], " ", h^-1, " ", kg^-1,
                ")"))
        })
    } else mo2.lab = Ylab
    if (Transparency) {
        Col = c(rgb(0, 0, 0, 0.7), "red", "orange")
    } else {
        Col = c(grey(0.3), "red", "orange")
    }
    Data = o2critobj$origData
    Data$Color = Col[1]
    Data$Color[o2critobj$lethalPoints] = Col[2]
    Data$Color[o2critobj$AddedPoints] = Col[3]
    # ordinary LS regression without added points: blue line, red symbols
    # ordinary LS regression with added points: blue line, red & orange symbols
    # regression through origin: green dotted line, red symbols
    line.color = ifelse(o2critobj$Method == "LS_reg", "blue", "darkgreen")
    line.type = ifelse(o2critobj$Method == "LS_reg", 1, 3)
    limX = c(0, max(Data$DO))
    limY = c(0, max(Data$MO2))
    plot(MO2 ~ DO, data = Data, xlim = limX, ylim = limY, col = Data$Color, xlab = Xlab,
        ylab = mo2.lab, ...)
    coord <- par("usr")
    if (plotID != "") {
        text(0, coord[4], plotID, cex = plotID.cex, adj = c(0, 1.2))
    }
    abline(h = smr, col = "orange")
    text(coord[1], smr, "SMR", adj = c(-0.1, 1.3), cex = smr.cex)
    text(coord[1], smr, round(smr, 1), adj = c(-0.1, -0.3), cex = smr.cex)
    if (!is.na(o2critobj$o2crit)) {
        abline(o2critobj$mod, col = line.color, lty = line.type)
        segments(o2critobj$o2crit, smr, o2critobj$o2crit, coord[3], col = line.color,
            lwd = 1)
        text(x = o2critobj$o2crit, y = 0, o2critobj$o2crit, col = line.color, cex = o2crit.cex,
            adj = c(-0.1, 0.5))
    }
}  # end of function

Working directories

Input

meta_files_wd: Directory for the metadata

wd <- getwd()
meta_files_wd <- paste0(wd, "./meta-data")  # creates a variable with the name of the wd we want to use

labchart_wd: Directory for Labchart estimated slopes

labchart_wd <- paste0(wd, "./lab-chart-slopes")

Output

output_fig_wd: this is where we will put the figures

output_fig_wd <- paste0(wd, "./output-fig")
ifelse(!dir.exists("output-fig"), dir.create("output-fig"), "Folder already exists")
## [1] "Folder already exists"

Input files

Slopes (MO2)

labchart_df: We have imported the slopes extracted in LabChart during each phase of the experiment

 setwd(labchart_wd)
# 
# # Get the names of all sheets in the Excel file
sheet_names <- excel_sheets("labchart-all-dates_v2.xlsx")
all_trials_select <- c("start_date", "order", "phase", "cycle", "date", "time")
labchart_list <- list()

for (sheet in sheet_names) {

  df <- read_excel("labchart-all-dates_v2.xlsx", sheet = sheet) %>% 
  dplyr::rename_with(tolower)
  
a_name <- paste0("a_", tolower(sheet))
a_df <- df %>%
  dplyr::select(starts_with('a'), all_trials_select) %>% 
  dplyr::rename(temp = a_temp) %>% 
  dplyr::mutate(across(starts_with('a'), as.numeric)) %>% 
  pivot_longer(
    cols = starts_with('a'), # Select all columns to pivot
    names_to = c("chamber_id", ".value"), # Separate column names into 'id' and other variables
    names_sep = "_"
  ) %>%
  dplyr::mutate(respirometer_group = "a") # Add a new column with a fixed value

labchart_list[[a_name]]<- a_df

b_name <- paste0("b_", tolower(sheet))
b_df <- df %>% 
  dplyr::select(starts_with('b'), all_trials_select) %>% 
  dplyr::rename(temp = b_temp) %>% 
  dplyr::mutate(across(starts_with('b'), as.numeric)) %>% 
  pivot_longer(
    cols = starts_with('b'), # Select all columns to pivot
    names_to = c("chamber_id", ".value"), # Separate column names into 'id' and other variables
    names_sep = "_"
  ) %>% 
    dplyr::mutate(respirometer_group = "b")

labchart_list[[b_name]] <- b_df

c_name <- paste0("c_", tolower(sheet))
c_df <- df %>% 
  dplyr::select(starts_with('c'), all_trials_select) %>% 
  dplyr::rename(temp = c_temp,
                i_cycle = cycle) %>% 
  dplyr::mutate(across(starts_with('c'), as.numeric)) %>%
  pivot_longer(
    cols = starts_with('c'), # Select all columns to pivot
    names_to = c("chamber_id", ".value"), # Separate column names into 'id' and other variables
    names_sep = "_"
  ) %>% 
    dplyr::mutate(respirometer_group = "c") %>% 
  dplyr::rename(cycle = i_cycle)

labchart_list[[c_name]] <- c_df

d_name <- paste0("d_", tolower(sheet))
d_df <- df %>% 
  dplyr::select(starts_with('d'), all_trials_select) %>% 
  dplyr::rename(temp = d_temp,
                i_date = date) %>% 
  dplyr::mutate(across(starts_with('d'), as.numeric)) %>%
  pivot_longer(
    cols = starts_with('d'), # Select all columns to pivot
    names_to = c("chamber_id", ".value"), # Separate column names into 'id' and other variables
    names_sep = "_"
  ) %>% 
    dplyr::mutate(respirometer_group = "d") %>% 
  dplyr::rename(date = i_date)

labchart_list[[d_name]] <- d_df
}


labchart_df <- bind_rows(labchart_list) %>% 
  dplyr::mutate(resp_cat_date = paste0(respirometer_group, "_", start_date),
                chamber_n = str_extract(chamber_id, "\\d+"),
                id_prox = paste0(resp_cat_date, "_", chamber_n),
                time_hms = as_hms(time*3600),
                date_chr = format(date, "%d/%m/%Y")
                )

Metadata

metadata: This is the meta data for each chamber

Note: We are also adding volume based on chamber type.

setwd(meta_files_wd)

metadata <- read_excel("Morpho.xlsx", na = "NA") %>%
    dplyr::mutate(id_split = id) %>%
    tidyr::separate(id_split, into = c("respirometer_group", "salinity_group", "start_date",
        "chamber"), sep = "_") %>%
    dplyr::mutate(volume = dplyr::case_when(chamber_type == "L" ~ 0.3, chamber_type ==
        "M_M" ~ 0.105, chamber_type == "M_NM" ~ 0.11, chamber_type == "S" ~ 0.058,
        chamber_type == "SM" ~ 0.075, chamber_type == "D3" ~ 0.055, TRUE ~ NA), id_prox = paste0(respirometer_group,
        "_", start_date, "_", chamber))

Combinding metadata

Adding the meta data to LabChart slopes

labchart_tidy <- labchart_df %>%
    dplyr::select(-start_date, -respirometer_group) %>%
    left_join(metadata, by = "id_prox") %>%
    dplyr::arrange(id)

Labchart data

We have 64 fish with MO2 data

n <- labchart_tidy %>%
    dplyr::filter(chamber_condition == "fish") %>%
    dplyr::distinct(id) %>%
    nrow(.)

paste0("n = ", n)
## [1] "n = 64"
labchart_tidy %>%
    dplyr::group_by(salinity_group) %>%
    dplyr::reframe(`n total` = length(unique(id))) %>%
    gt() %>%
    cols_label(salinity_group = "Salinity group") %>%
    cols_align(align = "center", columns = everything())
Salinity group n total
0 48
9 48

Filtering trials

We will remove some triasl which had errors. These are as follows:

  • A_0_25nov_3 needs to be removed (fish died)
  • B_0_26nov_4 flatlined early
  • C_0_22nov_2 accidentally opened the chamber early
  • C_9_26nov_2 stopped trial early, took too long
  • C_9_26nov_4 stopped trial early, took too long
  • D_9_27nov_3 sensor was jumpy and end points were hard to confidently ID visually

Remove list

remove_trial_error <- c("A_0_25nov_3", "B_0_26nov_4", "C_0_22nov_2", "C_9_26nov_2",
    "C_9_26nov_4", "D_9_27nov_3")


labchart_tidy <- labchart_tidy %>%
    dplyr::filter(!(id %in% remove_trial_error))

Filtering MO2

Here we apply the following filters to the MO2 data:

  • Remove the first 5 SMR cycles (burn in)
  • ?Remove all positive MO2 (data has not been subject to any transformations yet) ; should we do this?
  • Remove all MO2 calculated using less then 60 data points (5 min)
  • Remove all MO2 calculated when phase 50 close (50c) had high O2 (o2 < 6)
cycle_burn <- 0:4

labchart_tidy_fish <- labchart_tidy %>%
    dplyr::filter(!(cycle %in% cycle_burn) & mo2corr < 0 & n > 60 & chamber_condition ==
        "fish")

# 50c remove case with high o2
labchart_tidy_fish <- labchart_tidy_fish %>%
    dplyr::filter(!(phase == "50c" & o2 > 6))  # Removing any period in 50c where o2 was to high (opened)

SMR

Here we will estimate SMR using the mean of the lowest 3 vaules

smr_3l_means <- labchart_tidy_fish %>%
  dplyr::group_by(id) %>% 
  dplyr::filter(phase == "smr") %>%
  dplyr::arrange(desc(mo2corr)) %>%
  dplyr::slice_head(n = 3)  %>% # Select the three lowest MO2
  dplyr::ungroup() %>% 
  dplyr::group_by(id) %>% 
  dplyr::reframe(smr_l3 = mean(mo2corr))

# Combine the processed "smr" phase with all other phases
labchart_tidy_fish <- labchart_tidy_fish %>%
  dplyr::left_join(., smr_3l_means, by = "id")


Here I am using the calcSMR function to estimate SMR. We use mean of the lowest normal distribution (MLND) where CVmlnd < 5.4 and the mean of the lower 20% quantile (q0.2) were CVmlnd > 5.4, as described in Chabot D, Steffensen JF, Farrell AP (2016) https://doi.org/10.1111/jfb.12845. If CVmlnd is not calculated we used q0.2.

Transforming MO2 vaules

Here we are transforming the MO2 vaules

  • MO2 = absolute value of the background and leak corrected mo2 slope from labchart (mo2corr)
  • MO2_g = MO2 divided by fish mass
  • SMR = absolute value of the mean of the three lowest MO2 during the SMR phase (smr_l3)
  • SMR_g = SMR divided by fish mass
  • SMR_CHABOT = absolute value of the SMR estimates using Chabot et al recommendations (smr_chabot)
  • SMR_g = SMR_CHABOT divided by fish mass
  • DO = dissolved oxygen percentage calculated from o2 values (mg/L) using the recorded temperature, salinity, and a constant atmospheric pressure (1013.25)

NOTE : need to chat about units and corrections. I know there are some parts below that are incorrect.

# Combine back into one data frame
labchart_tidy_fish <- labchart_tidy_fish %>% 
    dplyr::mutate(MO2 = abs(mo2corr),
                  MO2_g = MO2/mass,
                  SMR = abs(smr_l3),
                  SMR_g = SMR/mass,
                  SMR_CHABOT = abs(smr_chabot),
                  SMR_CHABOT_g = SMR_CHABOT/mass,
                  DO = conv_o2(
                  o2 = o2,
                  from = "mg_per_l",
                  to = "percent_a.s.",
                  temp = temp, #C
                  sal = measured_salinity,
                  atm_pres = 1013.25),
                  net_volume = volume - mass, # Following instructions from Luis
                  MO2_BG = abs(mo2*net_volume*60*60), # Following instructions from Luis 
                  BG = bground*volume*60*60, # Following instructions from Luis (would need to add leak data)
                  MO2_adj = MO2_BG + BG, # Following instructions from Luis 
    )

Visualise

Here we plot all MO2 data. This is the absolute MO2, corrected for background respiration and any leaking that occurred down at low oxygen levels.

labchart_tidy_fish %>% 
  dplyr::filter(chamber_condition == "fish") %>% 
    ggplot(aes(y = MO2_g, x = o2, colour = id)) + # Default aesthetics
  geom_point(show.legend = FALSE) +
  geom_smooth(aes(group = id), method = "lm", se = FALSE, colour = scales::alpha("black", 0.5)) + # Transparent black lines
  geom_smooth(method = "lm", se = TRUE, colour = "red") + # Overall smooth line
  geom_smooth(se = TRUE, colour = "red", linetype = "dashed") +
  theme_clean() +
  labs(
    subtitle = "All values",
    x = "O2",
    y = "MO2 (g)"
  )


Looking at the difference responses in the two salinity groups. It’s appears more variable in freshwater.

labchart_tidy_fish %>% 
  dplyr::filter(chamber_condition == "fish") %>% 
    ggplot(aes(y = MO2_g, x = o2, colour = id)) + # Default aesthetics
  geom_point(show.legend = FALSE) +
  geom_smooth(aes(group = id), method = "lm", se = FALSE, colour = scales::alpha("black", 0.5)) + # Transparent black lines
  geom_smooth(method = "lm", se = TRUE, colour = "red") + # Overall smooth line
  geom_smooth(se = TRUE, colour = "red", linetype = "dashed") +
  theme_clean() +
  facet_wrap(~salinity_group) +
  labs(
    subtitle = "mo2 vs o2 by salinity treatment",
    x = "o2",
    y = "mo2 (g)"
  )


Looking at the difference chamber types

labchart_tidy_fish %>% 
  dplyr::filter(chamber_condition == "fish") %>% 
    ggplot(aes(y = MO2_g, x = o2, colour = id)) + # Default aesthetics
  geom_point(show.legend = FALSE) +
  geom_smooth(aes(group = id), method = "lm", se = FALSE, colour = scales::alpha("black", 0.5)) + # Transparent black lines
  geom_smooth(method = "lm", se = TRUE, colour = "red") + # Overall smooth line
  geom_smooth(se = TRUE, colour = "red", linetype = "dashed") +
  theme_clean() +
  facet_wrap(~chamber_type) +
  labs(
    subtitle = "mo2 vs o2 by salinity treatment",
    x = "o2",
    y = "mo2 (g)"
  )


Plotting MO2 estimates for each fish. The dashed red line is Chabot SMR methods, and the solid line is the mean of the lowest 3 measures (excluding the first 5 cycles)

NOTES :
-There’s something wired going on with a_0_25nov_2 it seems like many of the raw MO2 values are positive. - Often there seems to be a low MO2 vaule at about 5 mg/L O2

# Create output directory if needed
output_fig_slopes_wd <- file.path(output_fig_wd, "slopes")
if (!dir.exists(output_fig_slopes_wd)) {
    dir.create(output_fig_slopes_wd)
}

ids <- labchart_tidy_fish %>%
    dplyr::distinct(id) %>%
    pull(id) %>%
    as.list()

MO2_plot_list <- list()

# 1) Open the PDF device once
pdf(file = file.path(output_fig_slopes_wd, "combined_slopes.pdf"), width = 8, height = 6)

# 2) Loop over IDs and create each plot
for (id_i in ids) {

    smr_chabot <- labchart_tidy_fish %>%
        dplyr::filter(id == id_i) %>%
        dplyr::slice(1) %>%
        dplyr::pull(SMR_CHABOT)

    smr_l3 <- labchart_tidy_fish %>%
        dplyr::filter(id == id_i) %>%
        dplyr::slice(1) %>%
        dplyr::pull(SMR)

    plot <- labchart_tidy_fish %>%
        dplyr::filter(id == id_i) %>%
        ggplot(aes(x = o2, y = MO2)) + geom_hline(yintercept = smr_chabot, linetype = "dashed",
        color = "darkred") + geom_hline(yintercept = smr_l3, color = "darkred") +
        geom_point(aes(colour = phase)) + theme_clean() + labs(subtitle = paste0(id_i,
        " slopes"), x = "Mean o2 (mg_per_l)", y = "abs(mo2) (mg_per_l)")

    # Instead of saving each plot separately, just print it
    print(plot)

    MO2_plot_list[[id_i]] <- plot
}

# 3) Close the PDF device *after* the loop
dev.off()
## png 
##   2
for (p in MO2_plot_list) {
    print(p)
}

Pcrit (O2crit)

Chabot method

Here we will calculate Pcrit using Chabot method and function calcO2crit. We are using our estimates for SMR (mean of lowest three)

ids <- labchart_tidy_fish %>%
    dplyr::distinct(id) %>%
    dplyr::pull()

pcrit_model_df_list <- list()
pcrit_models <- list()

for (id_i in ids) {

    df_i <- labchart_tidy_fish %>%
        dplyr::filter(id == id_i)

    o2crit <- calcO2crit(Data = df_i, SMR = df_i$SMR[1], lowestMO2 = NA, gapLimit = 4,
        max.nb.MO2.for.reg = 7)

    vaule <- o2crit$o2crit
    nb_mo2_conforming <- o2crit$Nb_MO2_conforming
    r2 <- o2crit$r2
    method <- o2crit$Method
    p <- o2crit$P[1]

    pcrit_model_df <- tibble(id = id_i, pcrit_vaule = vaule, pcrit_nb_mo2_conforming = nb_mo2_conforming,
        pcrit_r2 = r2, pcrit_method = method, pcrit_p = p)

    pcrit_model_df_list[[id_i]] <- pcrit_model_df

    pcrit_models[[id_i]] <- o2crit

}

pcrit_model_df <- bind_rows(pcrit_model_df_list)

Ploting o2 crit

Here’s the plots for the Pcrit estimates

# Create output directory if needed
output_fig_pcrit_chabot_wd <- file.path(output_fig_wd, "model_chabot")
if (!dir.exists(output_fig_pcrit_chabot_wd)) {
    dir.create(output_fig_pcrit_chabot_wd)
}

ids <- labchart_tidy_fish %>%
    dplyr::distinct(id) %>%
    dplyr::pull()

pcrit_chabot_list <- list()

# Open a single PDF device
pdf(file = file.path(output_fig_pcrit_chabot_wd, "combined_chabot_plots.pdf"), width = 8,
    height = 6)

for (id_i in ids) {

    r2 <- pcrit_model_df %>%
        dplyr::filter(id == id_i) %>%
        dplyr::mutate(pcrit_r2 = round(pcrit_r2, 3)) %>%
        dplyr::pull(pcrit_r2)

    # Generate and render the plot
    plotO2crit(o2critobj = pcrit_models[[id_i]])

    # Add a title
    mtext(text = paste0(id_i, " (R2 = ", r2, ")"), side = 3, line = 2, adj = 0, col = "blue",
        font = 2, cex = 1.2)
}

# Close the PDF device *after* the loop
dev.off()
## png 
##   2


Printing in htlm document

ids <- labchart_tidy_fish %>%
    dplyr::distinct(id) %>%
    dplyr::pull()

for (id_i in ids) {

    r2 <- pcrit_model_df %>%
        dplyr::filter(id == id_i) %>%
        dplyr::mutate(pcrit_r2 = round(pcrit_r2, 3)) %>%
        dplyr::pull(pcrit_r2)

    # Generate and render the plot
    plotO2crit(o2critobj = pcrit_models[[id_i]])

    # Add a title
    mtext(text = paste0(id_i, " (R2 = ", r2, ")"), side = 3, line = 2, adj = 0, col = "blue",
        font = 2, cex = 1.2)
}

calc_pcrit()

Here using the 100 closed trials we will estimate Pcrit (commonly understood as the threshold below which oxygen consumption rate can no longer be sustained), based on paired PO2 and MO2 values with five popular techniques for Pcrit calculation: the traditional breakpoint metric (broken stick regression), the nonlinear regression metric (Marshall et al. 2013), the sub-prediction interval metric (Birk et al. 2019), the alpha-based Pcrit method (Seibel et al. 2021), and the linear low O2 (LLO) method (Reemeyer & Rees 2019).

https://search.r-project.org/CRAN/refmans/respirometry/html/calc_pcrit.html

Marshall et al (2013) suggest nonlinear regression (NLR)

Here’s a function to calculate Pcrit, we are using a function called calc_pcrit from the respirmetery package.

!parameters!

Parameters to consider

  • avg_top_n: for alpha method, a numeric value representing the number of top α0 (MO2/PO2) values to average together to estimate α. Default is 1. We recommend no more than 3 to avoid diminishing the α value with sub-maximal observations.

  • level: for Sub_PI method, Percentage at which the prediction interval should be constructed.

  • iqr: Only for Sub_PI. Removes mo2 observations that are this many interquartile ranges away from the mean value for the oxyregulating portion of the trial. If this filtering is not desired, set to infinity.

  • NLR_m: only applies to NLR. Pcrit is defined as the PO2 at which the slope of the best fitting function equals NLR_m (after the MO2 data are normalized to the 90% quantile). Default is 0.065

  • MR: A numeric value for the metabolic rate at which pcrit_alpha and pcrit_LLO should be returned. If not supplied by the user, then the mean MO2 of the “oxyregulating” portion of the curve is applied for pcrit_alpha and NA is returned for pcrit_LLO.

  • mo2_threshold: A single numeric value above which mo2 values are ignored for alpha Pcrit estimation. Useful to removing obviously erroneous values. Default is Inf.

Formate data

We will only use 100 c trails for this.

labchart_tidy_fish_100c <- labchart_tidy_fish %>%
    dplyr::filter(phase == "100c")

labchart_tidy_fish_100c_n <- labchart_tidy_fish_100c %>%
    dplyr::distinct(id) %>%
    nrow(.)

paste0("n for 100 closed = ", labchart_tidy_fish_100c_n)
## [1] "n for 100 closed = 23"


First we will build a model with 3 SMR values and all the 75c and 50c (or 100c) data

combined_pcirt_list <- list()

ids <- labchart_tidy_fish_100c %>% 
  dplyr::distinct(id) %>% 
  pull(id) %>% 
  as.list()


for (id_i in ids) {

  id_name <- id_i
  
  mo2_data <- labchart_tidy_fish_100c %>% 
    dplyr::filter(id == id_i)
  
  MR_set <- mo2_data$SMR[1] %>% as.numeric()
  
  # Use tryCatch to handle errors and skip problematic calculations
  pcrit_df <- tryCatch({
    
    pcrit_df <- calc_pcrit(po2 = mo2_data$o2, 
           mo2 = mo2_data$MO2, 
           method = 'All',
           avg_top_n = 2, # alpha metric (default = 1) recommend no more than 3
           level = 0.95, # Sub_PI metric (default = 0.95)
           iqr = 1.5, # Sub_PI metric (default = 1.5)
           NLR_m = 0.065, # NLR metric (default = 0.065)
           MR = MR_set, # alpha and LLO metrics,
           mo2_threshold = Inf, # alpha metric
           return_models = FALSE # return model parameters?
           ) %>%
      as.data.frame() %>%
      rownames_as_column(var = "method") %>%
      rename(value = ".") %>%
      tidyr::pivot_wider(.,
                     names_from = method,
                     values_from = value) %>%
      dplyr::mutate(id = id_name) %>%
      dplyr::select(id, everything())
    
  }, error = function(e) {
    message("Skipping channel ", id_name, " due to error: ", conditionMessage(e))
    NULL
  })
  
  # Only add to list if pcrit_df is not NULL
  if (!is.null(pcrit_df)) {
    combined_pcirt_list[[id_name]] <- pcrit_df
  }
}
## breakpoint estimate(s): 8.386752


Combined all the Pcrit model estimates together

pcirt <- bind_rows(combined_pcirt_list)

Plot Pcrit

Here we will plot the various Pcrit curves

# Create output directory if needed
output_fig_pcrit_100c_wd <- file.path(output_fig_wd, "pcrit-100c")
if (!dir.exists(output_fig_pcrit_100c_wd)) {
  dir.create(output_fig_pcrit_100c_wd)
}

ids <- labchart_tidy_fish_100c %>% 
  dplyr::distinct(id) %>% 
  pull(id) %>% 
  as.list()

# Open a single PDF device once
pdf(file = file.path(output_fig_pcrit_100c_wd, "combined_pcrit_plots.pdf"), 
    width = 8, height = 6)

for (id_i in ids) {
  
  id_name <- id_i
  
  mo2_data <- labchart_tidy_fish_100c %>% 
    dplyr::filter(id == id_i)
  
  MR_set <- mo2_data$SMR[1] %>% as.numeric()
  
  tryCatch({
    # Generate and render the plot
    plot_pcrit(
      po2 = mo2_data$o2, 
      mo2 = mo2_data$MO2, 
      method = 'All',
      avg_top_n = 1, 
      level = 0.95, 
      iqr = 1.5, 
      NLR_m = 0.065, 
      MR = MR_set, 
      mo2_threshold = Inf, 
      return_models = FALSE, 
      showNLRs = FALSE
    )
    
    # Add a title in the top-left corner
    mtext(text = paste(id_name),
          side = 3, line = 2, adj = 0, # Top margin, aligned to left
          col = "blue", font = 2, cex = 1.2)
    
  }, error = function(e) {
    message("Skipping channel ", id_name, " due to error: ", conditionMessage(e))
  })
}
## breakpoint estimate(s): 8.386752
# Close the PDF device *after* the loop
dev.off()
## png 
##   2


Plotting in the html

ids <- labchart_tidy_fish_100c %>% 
  dplyr::distinct(id) %>% 
  pull(id) %>% 
  as.list()

for (id_i in ids) {
  
  id_name <- id_i
  
  mo2_data <- labchart_tidy_fish_100c %>% 
    dplyr::filter(id == id_i)
  
  MR_set <- mo2_data$SMR[1] %>% as.numeric()
  
  tryCatch({
    # Generate and render the plot
    plot_pcrit(
      po2 = mo2_data$o2, 
      mo2 = mo2_data$MO2, 
      method = 'All',
      avg_top_n = 1, 
      level = 0.95, 
      iqr = 1.5, 
      NLR_m = 0.065, 
      MR = MR_set, 
      mo2_threshold = Inf, 
      return_models = FALSE, 
      showNLRs = FALSE
    )
    
    # Add a title in the top-left corner
    mtext(text = paste(id_name),
          side = 3, line = 2, adj = 0, # Top margin, aligned to left
          col = "blue", font = 2, cex = 1.2)
    
  }, error = function(e) {
    message("Skipping channel ", id_name, " due to error: ", conditionMessage(e))
  })
}

## breakpoint estimate(s): 8.386752

LS0tDQp0aXRsZTogImdtYWMtbGFiLWNoYXJ0Ig0KYXV0aG9yOiAiSmFrZSBNYXJ0aW4iDQpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlQiAlWScpYCINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgZGVwdGg6IDQNCiAgICBudW1iZXJfc2VjdGlvbnM6IG5vDQogICAgdGhlbWU6ICBjb3Ntbw0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgcGRmX2RvY3VtZW50Og0KICAgIHRvYzogeWVzDQprbml0OiB8DQogIChmdW5jdGlvbihpbnB1dCwgLi4uKSB7DQogICAgcm1hcmtkb3duOjpyZW5kZXIoDQogICAgICBpbnB1dCwNCiAgICAgIG91dHB1dF9maWxlID0gcGFzdGUwKA0KICAgICAgICdpbmRleC5odG1sJw0KICAgICAgKSwNCiAgICAgIGVudmlyID0gZ2xvYmFsZW52KCkNCiAgICApDQogIH0pDQotLS0NCg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyBSRUFEIE1FIA0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KDQoqKlNVTU1BUlkqKiA8YnI+DQpUaGlzIFIgY29kZSBpcyB1c2VkIHRvIGVzdGltYXRlIE1PMiBhbmQgUGNyaXQgKGNvbW1vbmx5IHVuZGVyc3Rvb2QgYXMgdGhlIHRocmVzaG9sZCBiZWxvdyB3aGljaCBveHlnZW4gY29uc3VtcHRpb24gcmF0ZSBjYW4gbm8gbG9uZ2VyIGJlIHN1c3RhaW5lZCkgaW4gdGhlIENvbW1vbiBnYWxheGlhcyAoR2FsYXhpYXMgbWFjdWxhdHVzKS4gVGhlIGFzc29jaWF0ZWQgYXJ0aWNsZSBpcyAiVGhlIHJvbGUgb2Ygb3Ntb3Jlc3BpcmF0b3J5IGNvbXByb21pc2UgaW4gaHlwb3hpYSB0b2xlcmFuY2Ugb2YgdGhlIHB1cnBvcnRlZGx5IG94eWNvbmZvcm1pbmcgdGVsZW9zdCAqR2FsYXhpYXMgbWFjdWxhdHVzKiIgPGJyPg0KDQoqKkFVVEhPUlMqKjxicj4NClRvIGJlIGFkZGVkDQo8YnI+DQoNCioqQUZGSUxJQVRJT05TKiogPGJyPg0KVG8gYmUgYWRkZWQNCjxicj4NCg0KKipBSU0qKiA8YnI+DQpUbyBiZSBhZGRlZA0KPGJyPg0KDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQojIEtuaXQgc2V0dGluZ3MgDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQoNClRoZXNlIGFyZSB0aGUgc2V0dGluZ3MgZm9yIHRoZSBodG1sIG91dHB1dC4gV2Ugd2lsbCB1c2UgdGhpcyB0byBtYWtlIG91dCBpbmRleCBmaWxlIG9uIEdpdA0KDQpgYGB7ciBzZXR1cH0NCiNrbml0ZXIgc2VldHRpbmcNCmtuaXRyOjpvcHRzX2NodW5rJHNldCgNCm1lc3NhZ2UgPSBGQUxTRSwNCndhcm5pbmcgPSBGQUxTRSwgIyBubyB3YXJuaW5ncw0KY2FjaGUgPSBUUlVFLCMgQ2FjaGVpbmcgdG8gc2F2ZSB0aW1lIHdoZW4ga25pdGluZw0KdGlkeSA9IFRSVUUNCikNCmBgYA0KDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQojIENvbnRhY3QNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCg0KKipKYWtlIE0uIE1hcnRpbioqIDxicj4NCg0KKipFbWFpbCoqOiBqYWtlLm1hcnRpbkBkZWFraW4uZWR1LmF1IChvciBqYWtlLm1hcnRpbi5yZXNlYXJjaEBnbWFpbC5jb20pIDxicj4NCg0KKipXZWIqKjogIGh0dHBzOi8vamFrZS5tYXJ0aW4ub3JnIDxicj4NCg0KKipHaXRIdWIqKjogaHR0cHM6Ly9naXRodWIuY29tL0pha2VNYXJ0aW5SZXNlYXJjaCA8YnI+DQoNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMgUmVxdWlyZWQgcGFja2FnZXMNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCg0KVGhlc2UgYXJlIHRoZSBSIHBhY2thZ2VzIHJlcXVpcmVkIGZvciB0aGlzIHNjcmlwdC4gWW91IHdpbGwgbmVlZCB0byBpbnN0YWxsIGEgcGFja2FnZSBjYWxsZWQgcGFjbWFuIHRvIHJ1biB0aGUgcF9sb2FkIGZ1bmN0aW9uLiANCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQ0KIyB0aGlzIGluc3RhbGxzIGFuZCBsb2FkIHBhY2thZ2VzDQojIG5lZWQgdG8gaW5zdGFsbCBwYWNtYW4NCnBhY21hbjo6cF9sb2FkKCJnZ3Bsb3QyIiwgDQogICAgICAgICAgICAgICAiZ2d0aGVtZXMiLCANCiAgICAgICAgICAgICAgICJnZ2ZvcnRpZnkiLCANCiAgICAgICAgICAgICAgICJndEV4dHJhcyIsIA0KICAgICAgICAgICAgICAgImlncmFwaCIsDQogICAgICAgICAgICAgICAiZGFnaXR0eSIsDQogICAgICAgICAgICAgICAiZ2dkYWciLA0KICAgICAgICAgICAgICAgImdncmlkZ2VzIiwNCiAgICAgICAgICAgICAgICJnZ2hhbHZlcyIsDQogICAgICAgICAgICAgICAiZ2dFeHRyYSIsDQogICAgICAgICAgICAgICAiZ3JpZEV4dHJhIiwNCiAgICAgICAgICAgICAgICJjb3JycGxvdCIsDQogICAgICAgICAgICAgICAiUkNvbG9yQnJld2VyIiwgDQogICAgICAgICAgICAgICAiZ3QiLCANCiAgICAgICAgICAgICAgICJndHN1bW1hcnkiLA0KICAgICAgICAgICAgICAgImdyaWQiLA0KICAgICAgICAgICAgICAgInBsb3RseSIsICMgZGF0YSB2aXN1YWxpc2F0aW9uDQogICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAidGlkeXZlcnNlIiwgDQogICAgICAgICAgICAgICAiamFuaXRvciIsIA0KICAgICAgICAgICAgICAgInJlYWR4bCIsIA0KICAgICAgICAgICAgICAgImJyb29tIiwgDQogICAgICAgICAgICAgICAiZGF0YS50YWJsZSIsIA0KICAgICAgICAgICAgICAgImRldnRvb2xzIiwNCiAgICAgICAgICAgICAgICJobXMiLCAjIGRhdGEgdGlkeQ0KICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAibWFyZ2luYWxlZmZlY3RzIiwgDQogICAgICAgICAgICAgICAiYnJtcyIsIA0KICAgICAgICAgICAgICAgInJzdGFuIiwgDQogICAgICAgICAgICAgICAicGVyZm9ybWFuY2UiLCANCiAgICAgICAgICAgICAgICJlbW1lYW5zIiwgDQogICAgICAgICAgICAgICAidGlkeWJheWVzIiwgDQogICAgICAgICAgICAgICAidmVnYW4iLA0KICAgICAgICAgICAgICAgImJldGFyZWciLA0KICAgICAgICAgICAgICAgImxtZTQiLCANCiAgICAgICAgICAgICAgICJjYXIiLCANCiAgICAgICAgICAgICAgICJsbWVyVGVzdCIsDQogICAgICAgICAgICAgICAicXFwbG90ciIsDQogICAgICAgICAgICAgICAicmVzcGlyb21ldHJ5IiwNCiAgICAgICAgICAgICAgICJtY2x1c3QiLA0KICAgICAgICAgICAgICAgIyBtb2RlbGxpbmcgDQogICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAiZGF0YXdpemFyZCIsIA0KICAgICAgICAgICAgICAgIlNSUyIgIyBkYXRhIG1hbmlwdWxhdGlvbiANCiAgICAgICAgICAgICAgICAgICAgICAgKQ0KYGBgDQoNCg0KIyMjIyMjIyMjIyMjIw0KIyBGdW5jdGlvbnMgKGN1c3RvbSkNCiMjIyMjIyMjIyMjIyMNCg0KSGVyZSBhcmUgc29tZSBjdXN0b20gZnVuY3Rpb24gdXNlZCB3aXRoaW4gdGhpcyBzY3JpcHQuIDxicj4gDQoNCioqY2FsY1NNUioqOiBhdXRob3JlZCBieSBDaGFib3QgRC4gdXNlZCB0byBlc3RpbWF0ZSBTTVIgd2l0aCBzZXZlcmFsIGRpZmZlcmVudCBtZXRob2RzDQoNCmBgYHtyfQ0KY2FsY1NNUiA9IGZ1bmN0aW9uKFksIHE9YygwLjEsMC4xNSwwLjIsMC4yNSwwLjMpLCBHPTE6NCl7DQoJdSA9IHNvcnQoWSkNCgl0aGUuTWNsdXN0IDwtIE1jbHVzdChZLCAgRz1HKQ0KCWNsIDwtIHRoZS5NY2x1c3QkY2xhc3NpZmljYXRpb24NCgkjIHNvbWV0aW1lcywgdGhlIGNsYXNzIGNvbnRhaW5pbmcgU01SIGlzIG5vdCBjYWxsZWQgMQ0KCSMgdGhlIGZvbGxvd2luZyBwcmVzdW1lcyB0aGF0IHdoZW4gY2xhc3MgMSBjb250YWlucyA+IDEwJSBvZiBjYXNlcywgDQoJIyBpdCBjb250YWlucyBTTVIsIG90aGVyd2lzZSB3ZSB0YWtlIGNsYXNzIDINCgljbDIgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShjbCkpDQoJY2wyJGNsIDwtIGFzLm51bWVyaWMobGV2ZWxzKGNsMiRjbCkpDQoJdmFsaWQgPC0gY2wyJEZyZXE+PTAuMSpsZW5ndGgodGltZSkgIA0KCXRoZS5jbCA8LSBtaW4oY2wyJGNsW3ZhbGlkXSkNCglsZWZ0LmRpc3RyIDwtIFlbdGhlLk1jbHVzdCRjbGFzc2lmaWNhdGlvbj09dGhlLmNsXQ0KCW1sbmQgPSB0aGUuTWNsdXN0JHBhcmFtZXRlcnMkbWVhblt0aGUuY2xdDQoJQ1ZtbG5kID0gc2QobGVmdC5kaXN0cikvbWxuZCAqIDEwMA0KCXF1YW50PXF1YW50aWxlKFksIHEpDQoJbG93MTA9bWVhbih1WzE6MTBdKQ0KCWxvdzEwcGMgPSBtZWFuKHVbNjooNSArIHJvdW5kKDAuMSoobGVuZ3RoKHUpLTUpKSldKQ0KCSMgcmVtb3ZlIDUgb3V0bGllcnMsIGtlZXAgbG93ZXN0IDEwJSBvZiB0aGUgcmVzdCwgYXZlcmFnZQ0KCSMgSGVycm1hbm4gJiBFbmRlcnMgMjAwMA0KCXJldHVybihsaXN0KG1sbmQ9bWxuZCwgcXVhbnQ9cXVhbnQsIGxvdzEwPWxvdzEwLCBsb3cxMHBjPWxvdzEwcGMsDQoJCSAgICAgIGNsPWNsLCBDVm1sbmQ9Q1ZtbG5kKSkNCn0NCmBgYA0KDQoNCioqY2FsY08yY3JpdCoqOiBhdXRob3JlZCBieSBDaGFib3QgRC4gdXNlZCB0byBlc3RpbWF0ZSBPMmNyaXQgKFBjcmlwdCkNCg0KYGBge3J9DQpjYWxjTzJjcml0IDwtIGZ1bmN0aW9uKERhdGEsIFNNUiwgbG93ZXN0TU8yPU5BLCBnYXBMaW1pdCA9IDQsDQptYXgubmIuTU8yLmZvci5yZWcgPSAyMCkNCnsNCiMgQVVUSE9SOiBEZW5pcyBDaGFib3QsIEluc3RpdHV0IE1hdXJpY2UtTGFtb250YWduZSwgREZPLCBDYW5hZGENCiMgZmlyc3QgdmVyc2lvbiB3cml0dGVuIGluIEp1bmUgMjAwOQ0KIyBsYXN0IHVwZGF0ZWQgaW4gSmFudWFyeSAyMDE1DQptZXRob2QgPSAiTFNfcmVnIiAjIHdpbGwgYmVjb21lICJ0aHJvdWdoX29yaWdpbiIgaWYgaW50ZXJjZXB0IGlzID4gMA0KaWYoaXMubmEobG93ZXN0TU8yKSkgbG93ZXN0TU8yID0gcXVhbnRpbGUoRGF0YSRNTzJbRGF0YSRETyA+PSA4MF0sIHA9MC4wNSkNCiMgU3RlcCAxOiBpZGVudGlmeSBwb2ludHMgd2hlcmUgTU8yIGlzIHByb3BvcnRpb25hbCB0byBETw0KZ2VxU01SID0gRGF0YSRNTzIgPj0gbG93ZXN0TU8yDQpwaXZvdERPID0gbWluKERhdGEkRE9bZ2VxU01SXSkNCmxldGhhbCA9IERhdGEkRE8gPCBwaXZvdERPDQpOX3VuZGVyX1NNUiA9IHN1bShsZXRoYWwpICMgcG9pbnRzIGF2YWlsYWJsZSBmb3IgcmVncmVzc2lvbj8NCmZpbmFsX05fdW5kZXJfU01SID0gbGV0aGFsICMgc29tZSBwb2ludHMgbWF5IGJlIHJlbW92ZWQgYXQgU3RlcCA0DQpsYXN0TU8ycmVnID0gRGF0YSRNTzJbRGF0YSRETyA9PSBwaXZvdERPXSAjIGxhc3QgTU8yIHdoZW4gcmVndWxhdGluZw0KaWYoTl91bmRlcl9TTVIgPiAxKSB0aGVNb2QgPSBsbShNTzJ+RE8sIGRhdGE9RGF0YVtsZXRoYWwsXSkNCiMgU3RlcCAyLCBhZGQgb25lIG9yIG1vcmUgcG9pbnQgYXQgb3IgYWJvdmUgU01SDQojIDJBLCB3aGVuIHRoZXJlIGFyZSBmZXdlciB0aGFuIDMgdmFsaWQgcG9pbnRzIHRvIGNhbGN1bGF0ZSBhIHJlZ3Jlc3Npb24NCmlmKE5fdW5kZXJfU01SIDwgMyl7DQptaXNzaW5nID0gMyAtIHN1bShsZXRoYWwpDQpub3QubGV0aGFsID0gRGF0YSRET1tnZXFTTVJdDQpET2xpbWl0ID0gbWF4KHNvcnQobm90LmxldGhhbClbMTptaXNzaW5nXSkgIyBoaWdoZXN0IERPIGFjY2VwdGFibGUNCiMgdG8gcmVhY2ggYSBOIG9mIDMNCmFkZGVkUG9pbnRzID0gRGF0YSRETyA8PSBET2xpbWl0DQpsZXRoYWwgPSBsZXRoYWwgfCBhZGRlZFBvaW50cw0KdGhlTW9kID0gbG0oTU8yfkRPLCBkYXRhPURhdGFbbGV0aGFsLF0pDQp9DQojIDJCLCBhZGQgcGl2b3RETyB0byB0aGUgZml0IHdoZW4gU3RlcCAxIHlpZWxkZWQgMyBvciBtb3JlIHZhbHVlcz8NCmlmKE5fdW5kZXJfU01SID49IDMpew0KbGV0aGFsQiA9IERhdGEkRE8gPD0gcGl2b3RETyAjIGhhcyBvbmUgbW9yZSB2YWx1ZSB0aGFuICJsZXRoYWwiDQpyZWdBID0gdGhlTW9kDQpyZWdCID0gbG0oTU8yfkRPLCBkYXRhPURhdGFbbGV0aGFsQixdKQ0KbGFyZ2Vfc2xvcGVfZHJvcCA9IChjb2VmKHJlZ0EpWzJdL2NvZWYocmVnQilbMl0pID4gMS4xICMgYXJiaXRyYXJ5DQpsYXJnZV9ET19nYXAgPSAobWF4KERhdGEkRE9bbGV0aGFsQl0pIC0gbWF4KERhdGEkRE9bbGV0aGFsXSkpID4gZ2FwTGltaXQNCnRvb1NtYWxsTU8yID0gbGFzdE1PMnJlZyA8IFNNUg0KaWYoIWxhcmdlX3Nsb3BlX2Ryb3AgJiAhbGFyZ2VfRE9fZ2FwICYgIXRvb1NtYWxsTU8yKSB7DQpsZXRoYWwgPSBsZXRoYWxCDQp0aGVNb2QgPSByZWdCDQp9ICMgb3RoZXJ3aXNlIHdlIGRvIG5vdCBhY2NlcHQgdGhlIGFkZGl0aW9uYWwgcG9pbnQNCn0NCiMgU3RlcCAzDQojIGlmIHRoZSB1c2VyIHdhbnRzIHRvIGxpbWl0IHRoZSBudW1iZXIgb2YgcG9pbnRzIGluIHRoZSByZWdyZXNzaW9uDQppZighaXMubmEobWF4Lm5iLk1PMi5mb3IucmVnKSAmIHN1bShsZXRoYWwpPm1heC5uYi5NTzIuZm9yLnJlZyl7DQpSYW5rcyA9IHJhbmsoRGF0YSRETykNCmxldGhhbCA9IFJhbmtzIDw9IG1heC5uYi5NTzIuZm9yLnJlZw0KdGhlTW9kID0gbG0oTU8yfkRPLCBkYXRhPURhdGFbbGV0aGFsLF0pDQpmaW5hbF9OX3VuZGVyX1NNUiA9IG1heC5uYi5NTzIuZm9yLnJlZw0KfQ0KIyBTdGVwIDQNCnByZWRNTzIgPSBhcy5udW1lcmljKHByZWRpY3QodGhlTW9kLCBkYXRhLmZyYW1lKERPPURhdGEkRE8pKSkNCkRhdGEkZGVsdGEgPSAoRGF0YSRNTzItcHJlZE1PMikvcHJlZE1PMiAqIDEwMCAjIHJlc2lkdWFscyBzZXQgdG8gemVybw0KIyB3aGVuIGJlbG93IHBpdm90RE8NCkRhdGEkZGVsdGFbRGF0YSRETyA8IHBpdm90RE8gfCBsZXRoYWxdID0gMA0KdG9sID0gMCAjIGFueSBwb3NpdGl2ZSByZXNpZHVhbCBpcyB1bmFjY2VwdGFibGUNCkhpZ2hWYWx1ZXMgPSBEYXRhJGRlbHRhID4gdG9sDQpSYW5rcyA9IHJhbmsoLTEqRGF0YSRkZWx0YSkNCkhpZ2hNTzIgPSBIaWdoVmFsdWVzICYgUmFua3MgPT0gbWluKFJhbmtzKSAjIGtlZXAgbGFyZ2VzdCByZXNpZHVhbA0KaWYgKHN1bShIaWdoVmFsdWVzKSA+IDApIHsNCm5ibGV0aGFsID0gc3VtKGxldGhhbCkNCkRhdGEkVyA9IE5BDQpEYXRhJFdbbGV0aGFsXT0xL25ibGV0aGFsDQpEYXRhJFdbSGlnaE1PMl0gPSAxDQp0aGVNb2QgPSBsbShNTzJ+RE8sIHdlaWdodD1XLCBkYXRhPURhdGFbbGV0aGFsIHwgSGlnaE1PMixdKQ0KIyBUaGlzIG5ldyByZWdyZXNzaW9uIGlzIGFsd2F5cyBhbiBpbXByb3ZlbWVudCwgYnV0IHRoZXJlIGNhbiBzdGlsbA0KIyBiZSBwb2ludHMgYWJvdmUgdGhlIGxpbmUsIHNvIHdlIHJlcGVhdA0KcHJlZE1PMl8yID0gYXMubnVtZXJpYyhwcmVkaWN0KHRoZU1vZCwgZGF0YS5mcmFtZShETz1EYXRhJERPKSkpDQpEYXRhJGRlbHRhMiA9IChEYXRhJE1PMi1wcmVkTU8yXzIpL3ByZWRNTzJfMiAqIDEwMA0KRGF0YSRkZWx0YTJbRGF0YSRETyA8IHBpdm90RE9dID0gMA0KdG9sID0gRGF0YSRkZWx0YTJbSGlnaE1PMl0NCkhpZ2hWYWx1ZXMyID0gRGF0YSRkZWx0YTIgPiB0b2wNCmlmKHN1bShIaWdoVmFsdWVzMik+MCl7DQpSYW5rczIgPSByYW5rKC0xKkRhdGEkZGVsdGEyKQ0KSGlnaE1PMl8yID0gSGlnaFZhbHVlczIgJiBSYW5rczIgPT0gMSAjIGtlZXAgdGhlIGxhcmdlc3QgcmVzaWR1YWwNCm5ibGV0aGFsID0gc3VtKGxldGhhbCkNCkRhdGEkVyA9IE5BDQpEYXRhJFdbbGV0aGFsXT0xL25ibGV0aGFsDQpEYXRhJFdbSGlnaE1PMl8yXSA9IDENCnRoZU1vZDIgPSBsbShNTzJ+RE8sIHdlaWdodD1XLCBkYXRhPURhdGFbbGV0aGFsIHwgSGlnaE1PMl8yLF0pDQojIGlzIG5ldyBzbG9wZSBzdGVlcGVyIHRoYW4gdGhlIG9sZCBvbmU/DQppZih0aGVNb2QyJGNvZWZbMl0gPiB0aGVNb2QkY29lZlsyXSkgew0KdGhlTW9kID0gdGhlTW9kMg0KSGlnaE1PMiA9IEhpZ2hNTzJfMg0KfQ0KfSAjIGVuZCBzZWNvbmQgc2VhcmNoIGZvciBoaWdoIHZhbHVlDQp9ICMgZW5kIGZpcnN0IHNlYXJjaCBmb3IgaGlnaCB2YWx1ZQ0KQ29lZiA9IGNvZWZmaWNpZW50cyh0aGVNb2QpDQojU3RlcCA1LCBjaGVjayBmb3IgcG9zaXRpdmUgaW50ZXJjZXB0DQpBYm92ZU9yaWdpbiA9IENvZWZbMV0gPiAwDQojIGlmIGl0IGlzLCB3ZSB1c2UgYSByZWdyZXNzaW9uIHRoYXQgZ29lcyB0aHJvdWdoIHRoZSBvcmlnaW4NCmlmIChBYm92ZU9yaWdpbil7DQp0aGVNb2QgPSBsbShNTzJ+RE8gLTEsIGRhdGE9RGF0YVtsZXRoYWwsXSkNCkNvZWYgPSBjKDAsIGNvZWZmaWNpZW50cyh0aGVNb2QpKSAjIG5lZWQgdG8gYWRkIHRoZSBpbnRlcmNlcHQgKDApDQojIG1hbnVhbGx5IHRvIGhhdmUgYSBwYWlyIG9mIGNvZWZmaWNpZW50cw0KbWV0aG9kID0gInRocm91Z2hfb3JpZ2luIg0KSGlnaE1PMiA9IHJlcChGQUxTRSwgbnJvdyhEYXRhKSkgIyBkaWQgbm90IHVzZSB0aGUgYWRkaXRpb25hbCB2YWx1ZQ0KIyBmcm9tIFN0ZXAgNA0KfQ0KcG8yY3JpdCA9IGFzLm51bWVyaWMocm91bmQoKFNNUiAtIENvZWZbMV0pIC8gQ29lZlsyXSwgMSkpDQpzdW1fbW9kID0gc3VtbWFyeSh0aGVNb2QpDQphbm92X21vZCA9IGFub3ZhKHRoZU1vZCkNCk8yQ1JJVCA9IGxpc3QobzJjcml0PXBvMmNyaXQsIFNNUj1TTVIsIE5iX01PMl9jb25mb3JtaW5nID0gTl91bmRlcl9TTVIsDQpOYl9NTzJfY29uZl91c2VkID0gZmluYWxfTl91bmRlcl9TTVIsDQpIaWdoX01PMl9yZXF1aXJlZCA9IHN1bShIaWdoTU8yKSA9PSAxLCBvcmlnRGF0YT1EYXRhLA0KTWV0aG9kPW1ldGhvZCwgbW9kPXRoZU1vZCwgcjIgPSBzdW1fbW9kJHIuc3F1YXJlZCwNClAgPSBhbm92X21vZCQiUHIoPkYpIiwgbGV0aGFsUG9pbnRzID0gd2hpY2gobGV0aGFsKSwNCkFkZGVkUG9pbnRzID0gd2hpY2goSGlnaE1PMikpDQp9ICMgZW5kIGZ1bmN0aW9uDQpgYGANCg0KDQoqKnBsb3RPMmNyaXQqKjogdXNlZCB0byBwbG90IHRoZSBtb2RlcyB1c2VkIGZvciB0aGUgY2FsY08yY3JpdCBmdW5jdGlvbg0KDQpgYGB7cn0NCnBsb3RPMmNyaXQgPC0gZnVuY3Rpb24obzJjcml0b2JqLCBwbG90SUQ9IiIsDQpYbGFiPSJEaXNzb2x2ZWQgb3h5Z2VuICglIHNhdC4pIiwgWWxhYj0iZG90aXRhbHVtb2wiLA0Kc21yLmNleD0wLjksIG8yY3JpdC5jZXg9MC45LCBwbG90SUQuY2V4PTEuMiwNClRyYW5zcGFyZW5jeT1ULC4uLikNCnsNCiMgQVVUSE9SOiBEZW5pcyBDaGFib3QsIEluc3RpdHV0IE1hdXJpY2UtTGFtb250YWduZSwgREZPLCBDYW5hZGENCiMgZmlyc3QgdmVyc2lvbiB3cml0dGVuIGluIEp1bmUgMjAwOQ0KIyBsYXN0IHVwZGF0ZWQgMjAxNS0wMi0wOQ0KIyBmb3IgUiBwbG90dGluZyBkZXZpY2VzIHRoYXQgZG8gbm90IHN1cHBvcnQgdHJhbnNwYXJlbmN5DQojIChlLmcuLCBwb3N0c2NyaXB0KSwgc2V0IFRyYW5zcGFyZW5jeSB0byBGQUxTRQ0Kc21yID0gbzJjcml0b2JqJFNNUg0KaWYoWWxhYiAlaW4lIGMoImRvdGl0YWx1bW9sIiwgIml0YWx1bW9sIiwgImRvdHVtb2wiLCAidW1vbCIsDQoiZG90aXRhbG1nIiwgIml0YWxtZyIsICJkb3RtZyIsICJtZyIpKSB7DQpzd2l0Y2goWWxhYiwNCmRvdGl0YWx1bW9sID0gew0KbW8yLmxhYiA9IGV4cHJlc3Npb24ocGFzdGUoaXRhbGljKGRvdChNKSlbT1syXV0sICIgKCIsbXUsIm1vbCAiLCBPWzJdLA0KIiAiLCBtaW5eLTEsICIgIiwga2deLTEsICIpIikpDQp9LA0KaXRhbHVtb2wgPSB7DQptbzIubGFiID0gZXhwcmVzc2lvbihwYXN0ZShpdGFsaWMoTSlbT1syXV0sICIgKCIsbXUsIm1vbCAiLCBPWzJdLCAiICIsDQptaW5eLTEsICIgIiwga2deLTEsICIpIikpDQp9LA0KZG90dW1vbCA9IHsNCm1vMi5sYWIgPSBleHByZXNzaW9uKHBhc3RlKGRvdChNKVtPWzJdXSwgIiAoIixtdSwibW9sICIsIE9bMl0sICIgIiwNCm1pbl4tMSwgIiAiLCBrZ14tMSwgIikiKSkNCn0sDQp1bW9sID0gew0KbW8yLmxhYiA9IGV4cHJlc3Npb24ocGFzdGUoTVtPWzJdXSwgIiAoIixtdSwibW9sICIsIE9bMl0sICIgIiwgbWluXi0xLA0KIiAiLCBrZ14tMSwgIikiKSkNCn0sDQpkb3RpdGFsbWcgPSB7DQptbzIubGFiID0gZXhwcmVzc2lvbihwYXN0ZShpdGFsaWMoZG90KE0pKVtPWzJdXSwgIiAobWcgIiwgT1syXSwgIiAiLA0KaF4tMSwgIiAiLCBrZ14tMSwgIikiKSkNCn0sDQppdGFsbWcgPSB7DQptbzIubGFiID0gZXhwcmVzc2lvbihwYXN0ZShpdGFsaWMoTSlbT1syXV0sICIgKG1nICIsIE9bMl0sICIgIiwNCmheLTEsICIgIiwga2deLTEsICIpIikpDQp9LA0KZG90bWcgPSB7DQptbzIubGFiID0gZXhwcmVzc2lvbihwYXN0ZShkb3QoTSlbT1syXV0sICIgKG1nICIsIE9bMl0sICIgIiwgaF4tMSwgIiAiLA0Ka2deLTEsICIpIikpDQp9LA0KbWcgPSB7DQptbzIubGFiID0gZXhwcmVzc2lvbihwYXN0ZShNW09bMl1dLCAiIChtZyAiLCBPWzJdLCAiICIsIGheLTEsICIgIiwNCmtnXi0xLCAiKSIpKQ0KfQ0KKQ0KfSBlbHNlIG1vMi5sYWI9WWxhYg0KaWYoVHJhbnNwYXJlbmN5KSB7Q29sPWMocmdiKDAsMCwwLDAuNyksICJyZWQiLCAib3JhbmdlIikNCn0gZWxzZSB7Q29sPWMoZ3JleSgwLjMpLCAicmVkIiwgIm9yYW5nZSIpfQ0KRGF0YT1vMmNyaXRvYmokb3JpZ0RhdGENCkRhdGEkQ29sb3IgPSBDb2xbMV0NCkRhdGEkQ29sb3JbbzJjcml0b2JqJGxldGhhbFBvaW50c10gPSBDb2xbMl0NCkRhdGEkQ29sb3JbbzJjcml0b2JqJEFkZGVkUG9pbnRzXSA9IENvbFszXQ0KIyBvcmRpbmFyeSBMUyByZWdyZXNzaW9uIHdpdGhvdXQgYWRkZWQgcG9pbnRzOiBibHVlIGxpbmUsIHJlZCBzeW1ib2xzDQojIG9yZGluYXJ5IExTIHJlZ3Jlc3Npb24gd2l0aCBhZGRlZCBwb2ludHM6IGJsdWUgbGluZSwgcmVkICYgb3JhbmdlIHN5bWJvbHMNCiMgcmVncmVzc2lvbiB0aHJvdWdoIG9yaWdpbjogZ3JlZW4gZG90dGVkIGxpbmUsIHJlZCBzeW1ib2xzDQpsaW5lLmNvbG9yID0gaWZlbHNlKG8yY3JpdG9iaiRNZXRob2Q9PSJMU19yZWciLCAiYmx1ZSIsICJkYXJrZ3JlZW4iKQ0KbGluZS50eXBlID0gaWZlbHNlKG8yY3JpdG9iaiRNZXRob2Q9PSJMU19yZWciLCAxLCAzKQ0KbGltWCA9IGMoMCwgbWF4KERhdGEkRE8pKQ0KbGltWSA9IGMoMCwgbWF4KERhdGEkTU8yKSkNCnBsb3QoTU8yfkRPLCBkYXRhPURhdGEsIHhsaW09bGltWCwgeWxpbT1saW1ZLCBjb2w9RGF0YSRDb2xvciwgeGxhYj1YbGFiLA0KeWxhYj1tbzIubGFiLCAuLi4pDQpjb29yZCA8LSBwYXIoInVzciIpDQppZihwbG90SUQgIT0gIiIpew0KdGV4dCgwLCBjb29yZFs0XSwgcGxvdElELCBjZXg9cGxvdElELmNleCwgYWRqPWMoMCwxLjIpKQ0KfQ0KYWJsaW5lKGg9c21yLCBjb2w9Im9yYW5nZSIpDQp0ZXh0KGNvb3JkWzFdLCBzbXIsICJTTVIiLCBhZGo9YygtMC4xLDEuMyksIGNleD1zbXIuY2V4KQ0KdGV4dChjb29yZFsxXSwgc21yLCByb3VuZChzbXIsMSksIGFkaj1jKC0wLjEsLTAuMyksIGNleD1zbXIuY2V4KQ0KaWYoIWlzLm5hKG8yY3JpdG9iaiRvMmNyaXQpKSB7DQphYmxpbmUobzJjcml0b2JqJG1vZCwgY29sPWxpbmUuY29sb3IsIGx0eT1saW5lLnR5cGUpDQpzZWdtZW50cyhvMmNyaXRvYmokbzJjcml0LCBzbXIsIG8yY3JpdG9iaiRvMmNyaXQsIGNvb3JkWzNdLA0KY29sPWxpbmUuY29sb3IsIGx3ZD0xKQ0KdGV4dCh4PW8yY3JpdG9iaiRvMmNyaXQsIHk9MCwgbzJjcml0b2JqJG8yY3JpdCwgY29sPWxpbmUuY29sb3IsDQpjZXg9bzJjcml0LmNleCwgYWRqPWMoLTAuMSwwLjUpKQ0KfQ0KfSAjIGVuZCBvZiBmdW5jdGlvbg0KYGBgDQoNCg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyBXb3JraW5nIGRpcmVjdG9yaWVzIA0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KDQojIyBJbnB1dA0KDQoqKm1ldGFfZmlsZXNfd2QqKjogRGlyZWN0b3J5IGZvciB0aGUgbWV0YWRhdGENCg0KYGBge3J9DQp3ZCA8LSBnZXR3ZCgpDQptZXRhX2ZpbGVzX3dkIDwtIHBhc3RlMCh3ZCwgIi4vbWV0YS1kYXRhIikgIyBjcmVhdGVzIGEgdmFyaWFibGUgd2l0aCB0aGUgbmFtZSBvZiB0aGUgd2Qgd2Ugd2FudCB0byB1c2UNCmBgYA0KDQoqKmxhYmNoYXJ0X3dkKio6IERpcmVjdG9yeSBmb3IgTGFiY2hhcnQgZXN0aW1hdGVkIHNsb3Blcw0KDQpgYGB7cn0NCmxhYmNoYXJ0X3dkIDwtIHBhc3RlMCh3ZCwgIi4vbGFiLWNoYXJ0LXNsb3BlcyIpDQpgYGANCg0KIyMgT3V0cHV0DQoNCioqb3V0cHV0X2ZpZ193ZCoqOiB0aGlzIGlzIHdoZXJlIHdlIHdpbGwgcHV0IHRoZSBmaWd1cmVzDQoNCmBgYHtyfQ0Kb3V0cHV0X2ZpZ193ZCA8LSBwYXN0ZTAod2QsICIuL291dHB1dC1maWciKQ0KaWZlbHNlKCFkaXIuZXhpc3RzKCJvdXRwdXQtZmlnIiksIGRpci5jcmVhdGUoIm91dHB1dC1maWciKSwgIkZvbGRlciBhbHJlYWR5IGV4aXN0cyIpDQpgYGANCg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyBJbnB1dCBmaWxlcw0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KDQojIyBTbG9wZXMgKE1PMikNCg0KKipsYWJjaGFydF9kZioqOiBXZSBoYXZlIGltcG9ydGVkIHRoZSBzbG9wZXMgZXh0cmFjdGVkIGluIExhYkNoYXJ0IGR1cmluZyBlYWNoIHBoYXNlIG9mIHRoZSBleHBlcmltZW50DQoNCmBgYHtyfQ0KIHNldHdkKGxhYmNoYXJ0X3dkKQ0KIyANCiMgIyBHZXQgdGhlIG5hbWVzIG9mIGFsbCBzaGVldHMgaW4gdGhlIEV4Y2VsIGZpbGUNCnNoZWV0X25hbWVzIDwtIGV4Y2VsX3NoZWV0cygibGFiY2hhcnQtYWxsLWRhdGVzX3YyLnhsc3giKQ0KYWxsX3RyaWFsc19zZWxlY3QgPC0gYygic3RhcnRfZGF0ZSIsICJvcmRlciIsICJwaGFzZSIsICJjeWNsZSIsICJkYXRlIiwgInRpbWUiKQ0KbGFiY2hhcnRfbGlzdCA8LSBsaXN0KCkNCg0KZm9yIChzaGVldCBpbiBzaGVldF9uYW1lcykgew0KDQogIGRmIDwtIHJlYWRfZXhjZWwoImxhYmNoYXJ0LWFsbC1kYXRlc192Mi54bHN4Iiwgc2hlZXQgPSBzaGVldCkgJT4lIA0KICBkcGx5cjo6cmVuYW1lX3dpdGgodG9sb3dlcikNCiAgDQphX25hbWUgPC0gcGFzdGUwKCJhXyIsIHRvbG93ZXIoc2hlZXQpKQ0KYV9kZiA8LSBkZiAlPiUNCiAgZHBseXI6OnNlbGVjdChzdGFydHNfd2l0aCgnYScpLCBhbGxfdHJpYWxzX3NlbGVjdCkgJT4lIA0KICBkcGx5cjo6cmVuYW1lKHRlbXAgPSBhX3RlbXApICU+JSANCiAgZHBseXI6Om11dGF0ZShhY3Jvc3Moc3RhcnRzX3dpdGgoJ2EnKSwgYXMubnVtZXJpYykpICU+JSANCiAgcGl2b3RfbG9uZ2VyKA0KICAgIGNvbHMgPSBzdGFydHNfd2l0aCgnYScpLCAjIFNlbGVjdCBhbGwgY29sdW1ucyB0byBwaXZvdA0KICAgIG5hbWVzX3RvID0gYygiY2hhbWJlcl9pZCIsICIudmFsdWUiKSwgIyBTZXBhcmF0ZSBjb2x1bW4gbmFtZXMgaW50byAnaWQnIGFuZCBvdGhlciB2YXJpYWJsZXMNCiAgICBuYW1lc19zZXAgPSAiXyINCiAgKSAlPiUNCiAgZHBseXI6Om11dGF0ZShyZXNwaXJvbWV0ZXJfZ3JvdXAgPSAiYSIpICMgQWRkIGEgbmV3IGNvbHVtbiB3aXRoIGEgZml4ZWQgdmFsdWUNCg0KbGFiY2hhcnRfbGlzdFtbYV9uYW1lXV08LSBhX2RmDQoNCmJfbmFtZSA8LSBwYXN0ZTAoImJfIiwgdG9sb3dlcihzaGVldCkpDQpiX2RmIDwtIGRmICU+JSANCiAgZHBseXI6OnNlbGVjdChzdGFydHNfd2l0aCgnYicpLCBhbGxfdHJpYWxzX3NlbGVjdCkgJT4lIA0KICBkcGx5cjo6cmVuYW1lKHRlbXAgPSBiX3RlbXApICU+JSANCiAgZHBseXI6Om11dGF0ZShhY3Jvc3Moc3RhcnRzX3dpdGgoJ2InKSwgYXMubnVtZXJpYykpICU+JSANCiAgcGl2b3RfbG9uZ2VyKA0KICAgIGNvbHMgPSBzdGFydHNfd2l0aCgnYicpLCAjIFNlbGVjdCBhbGwgY29sdW1ucyB0byBwaXZvdA0KICAgIG5hbWVzX3RvID0gYygiY2hhbWJlcl9pZCIsICIudmFsdWUiKSwgIyBTZXBhcmF0ZSBjb2x1bW4gbmFtZXMgaW50byAnaWQnIGFuZCBvdGhlciB2YXJpYWJsZXMNCiAgICBuYW1lc19zZXAgPSAiXyINCiAgKSAlPiUgDQogICAgZHBseXI6Om11dGF0ZShyZXNwaXJvbWV0ZXJfZ3JvdXAgPSAiYiIpDQoNCmxhYmNoYXJ0X2xpc3RbW2JfbmFtZV1dIDwtIGJfZGYNCg0KY19uYW1lIDwtIHBhc3RlMCgiY18iLCB0b2xvd2VyKHNoZWV0KSkNCmNfZGYgPC0gZGYgJT4lIA0KICBkcGx5cjo6c2VsZWN0KHN0YXJ0c193aXRoKCdjJyksIGFsbF90cmlhbHNfc2VsZWN0KSAlPiUgDQogIGRwbHlyOjpyZW5hbWUodGVtcCA9IGNfdGVtcCwNCiAgICAgICAgICAgICAgICBpX2N5Y2xlID0gY3ljbGUpICU+JSANCiAgZHBseXI6Om11dGF0ZShhY3Jvc3Moc3RhcnRzX3dpdGgoJ2MnKSwgYXMubnVtZXJpYykpICU+JQ0KICBwaXZvdF9sb25nZXIoDQogICAgY29scyA9IHN0YXJ0c193aXRoKCdjJyksICMgU2VsZWN0IGFsbCBjb2x1bW5zIHRvIHBpdm90DQogICAgbmFtZXNfdG8gPSBjKCJjaGFtYmVyX2lkIiwgIi52YWx1ZSIpLCAjIFNlcGFyYXRlIGNvbHVtbiBuYW1lcyBpbnRvICdpZCcgYW5kIG90aGVyIHZhcmlhYmxlcw0KICAgIG5hbWVzX3NlcCA9ICJfIg0KICApICU+JSANCiAgICBkcGx5cjo6bXV0YXRlKHJlc3Bpcm9tZXRlcl9ncm91cCA9ICJjIikgJT4lIA0KICBkcGx5cjo6cmVuYW1lKGN5Y2xlID0gaV9jeWNsZSkNCg0KbGFiY2hhcnRfbGlzdFtbY19uYW1lXV0gPC0gY19kZg0KDQpkX25hbWUgPC0gcGFzdGUwKCJkXyIsIHRvbG93ZXIoc2hlZXQpKQ0KZF9kZiA8LSBkZiAlPiUgDQogIGRwbHlyOjpzZWxlY3Qoc3RhcnRzX3dpdGgoJ2QnKSwgYWxsX3RyaWFsc19zZWxlY3QpICU+JSANCiAgZHBseXI6OnJlbmFtZSh0ZW1wID0gZF90ZW1wLA0KICAgICAgICAgICAgICAgIGlfZGF0ZSA9IGRhdGUpICU+JSANCiAgZHBseXI6Om11dGF0ZShhY3Jvc3Moc3RhcnRzX3dpdGgoJ2QnKSwgYXMubnVtZXJpYykpICU+JQ0KICBwaXZvdF9sb25nZXIoDQogICAgY29scyA9IHN0YXJ0c193aXRoKCdkJyksICMgU2VsZWN0IGFsbCBjb2x1bW5zIHRvIHBpdm90DQogICAgbmFtZXNfdG8gPSBjKCJjaGFtYmVyX2lkIiwgIi52YWx1ZSIpLCAjIFNlcGFyYXRlIGNvbHVtbiBuYW1lcyBpbnRvICdpZCcgYW5kIG90aGVyIHZhcmlhYmxlcw0KICAgIG5hbWVzX3NlcCA9ICJfIg0KICApICU+JSANCiAgICBkcGx5cjo6bXV0YXRlKHJlc3Bpcm9tZXRlcl9ncm91cCA9ICJkIikgJT4lIA0KICBkcGx5cjo6cmVuYW1lKGRhdGUgPSBpX2RhdGUpDQoNCmxhYmNoYXJ0X2xpc3RbW2RfbmFtZV1dIDwtIGRfZGYNCn0NCg0KDQpsYWJjaGFydF9kZiA8LSBiaW5kX3Jvd3MobGFiY2hhcnRfbGlzdCkgJT4lIA0KICBkcGx5cjo6bXV0YXRlKHJlc3BfY2F0X2RhdGUgPSBwYXN0ZTAocmVzcGlyb21ldGVyX2dyb3VwLCAiXyIsIHN0YXJ0X2RhdGUpLA0KICAgICAgICAgICAgICAgIGNoYW1iZXJfbiA9IHN0cl9leHRyYWN0KGNoYW1iZXJfaWQsICJcXGQrIiksDQogICAgICAgICAgICAgICAgaWRfcHJveCA9IHBhc3RlMChyZXNwX2NhdF9kYXRlLCAiXyIsIGNoYW1iZXJfbiksDQogICAgICAgICAgICAgICAgdGltZV9obXMgPSBhc19obXModGltZSozNjAwKSwNCiAgICAgICAgICAgICAgICBkYXRlX2NociA9IGZvcm1hdChkYXRlLCAiJWQvJW0vJVkiKQ0KICAgICAgICAgICAgICAgICkNCmBgYA0KDQojIyBNZXRhZGF0YQ0KDQoqKm1ldGFkYXRhKio6IFRoaXMgaXMgdGhlIG1ldGEgZGF0YSBmb3IgZWFjaCBjaGFtYmVyIDxicj4NCg0KKk5vdGUqOiBXZSBhcmUgYWxzbyBhZGRpbmcgdm9sdW1lIGJhc2VkIG9uIGNoYW1iZXIgdHlwZS4NCg0KYGBge3J9DQpzZXR3ZChtZXRhX2ZpbGVzX3dkKQ0KDQptZXRhZGF0YSA8LSByZWFkX2V4Y2VsKCJNb3JwaG8ueGxzeCIsIG5hID0gIk5BIikgJT4lIA0KICBkcGx5cjo6bXV0YXRlKGlkX3NwbGl0ID0gaWQpICU+JSANCiAgdGlkeXI6OnNlcGFyYXRlKGlkX3NwbGl0LCBpbnRvID0gYygicmVzcGlyb21ldGVyX2dyb3VwIiwgInNhbGluaXR5X2dyb3VwIiwgInN0YXJ0X2RhdGUiLCAiY2hhbWJlciIpLCBzZXAgPSAiXyIpICU+JSANCiAgZHBseXI6Om11dGF0ZSgNCiAgICAgIHZvbHVtZSA9IGRwbHlyOjpjYXNlX3doZW4oDQogICAgICAgIGNoYW1iZXJfdHlwZSA9PSAiTCIgfiAwLjMwMCwNCiAgICAgICAgY2hhbWJlcl90eXBlID09ICJNX00iIH4gMC4xMDUsDQogICAgICAgIGNoYW1iZXJfdHlwZSA9PSAiTV9OTSIgfiAwLjExLA0KICAgICAgICBjaGFtYmVyX3R5cGUgPT0gIlMiIH4gMC4wNTgsDQogICAgICAgIGNoYW1iZXJfdHlwZSA9PSAiU00iIH4gMC4wNzUsDQogICAgICAgIGNoYW1iZXJfdHlwZSA9PSAiRDMiIH4gMC4wNTUsDQogICAgICAgIFRSVUUgfiBOQQ0KICAgICAgKSwNCiAgICAgIGlkX3Byb3ggPSBwYXN0ZTAocmVzcGlyb21ldGVyX2dyb3VwLCAiXyIsIHN0YXJ0X2RhdGUsICJfIiwgY2hhbWJlcikpDQpgYGANCg0KDQojIyMgQ29tYmluZGluZyBtZXRhZGF0YQ0KDQpBZGRpbmcgdGhlIG1ldGEgZGF0YSB0byBMYWJDaGFydCBzbG9wZXMNCg0KYGBge3J9DQpsYWJjaGFydF90aWR5IDwtIGxhYmNoYXJ0X2RmICU+JSANCiAgZHBseXI6OnNlbGVjdCgtc3RhcnRfZGF0ZSwgLXJlc3Bpcm9tZXRlcl9ncm91cCkgJT4lIA0KICBsZWZ0X2pvaW4obWV0YWRhdGEsIGJ5ID0gImlkX3Byb3giKSAlPiUgDQogIGRwbHlyOjphcnJhbmdlKGlkKQ0KYGBgDQoNCg0KIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyBMYWJjaGFydCBkYXRhDQojIyMjIyMjIyMjIyMjIyMjIyMjDQoNCldlIGhhdmUgKio2NCBmaXNoKiogd2l0aCBNTzIgZGF0YQ0KDQpgYGB7cn0NCm4gPC0gbGFiY2hhcnRfdGlkeSAlPiUgDQogIGRwbHlyOjpmaWx0ZXIoY2hhbWJlcl9jb25kaXRpb24gPT0gImZpc2giKSAlPiUgDQogIGRwbHlyOjpkaXN0aW5jdChpZCkgJT4lIA0KICBucm93KC4pDQoNCnBhc3RlMCgibiA9ICIsIG4pDQpgYGANCg0KYGBge3J9DQpsYWJjaGFydF90aWR5ICU+JSANCiAgZHBseXI6Omdyb3VwX2J5KHNhbGluaXR5X2dyb3VwKSAlPiUgDQogIGRwbHlyOjpyZWZyYW1lKCduIHRvdGFsJyA9IGxlbmd0aCh1bmlxdWUoaWQpKSkgJT4lIA0KICBndCgpICU+JSANCiAgY29sc19sYWJlbCgNCiAgICBzYWxpbml0eV9ncm91cCA9ICJTYWxpbml0eSBncm91cCINCiAgKSAlPiUgDQogIGNvbHNfYWxpZ24oDQogICAgYWxpZ24gPSAiY2VudGVyIiwgDQogICAgY29sdW1ucyA9IGV2ZXJ5dGhpbmcoKQ0KICApDQpgYGANCg0KIyMgRmlsdGVyaW5nIHRyaWFscw0KDQpXZSB3aWxsIHJlbW92ZSBzb21lIHRyaWFzbCB3aGljaCBoYWQgZXJyb3JzLiBUaGVzZSBhcmUgYXMgZm9sbG93czogPGJyPg0KDQotCUFfMF8yNW5vdl8zIG5lZWRzIHRvIGJlIHJlbW92ZWQgKGZpc2ggZGllZCkNCi0JQl8wXzI2bm92XzQgZmxhdGxpbmVkIGVhcmx5DQotCUNfMF8yMm5vdl8yIGFjY2lkZW50YWxseSBvcGVuZWQgdGhlIGNoYW1iZXIgZWFybHkNCi0JQ185XzI2bm92XzIgc3RvcHBlZCB0cmlhbCBlYXJseSwgdG9vayB0b28gbG9uZw0KLQlDXzlfMjZub3ZfNCBzdG9wcGVkIHRyaWFsIGVhcmx5LCB0b29rIHRvbyBsb25nDQotCURfOV8yN25vdl8zIHNlbnNvciB3YXMganVtcHkgYW5kIGVuZCBwb2ludHMgd2VyZSBoYXJkIHRvIGNvbmZpZGVudGx5IElEIHZpc3VhbGx5IDxicj4NCg0KUmVtb3ZlIGxpc3QgDQoNCmBgYHtyfQ0KcmVtb3ZlX3RyaWFsX2Vycm9yIDwtIGMoIkFfMF8yNW5vdl8zIiwgIkJfMF8yNm5vdl80IiwgIkNfMF8yMm5vdl8yIiwgIkNfOV8yNm5vdl8yIiwgIkNfOV8yNm5vdl80IiwgIkRfOV8yN25vdl8zIikNCmBgYA0KPGJyPg0KDQpgYGB7cn0NCmxhYmNoYXJ0X3RpZHkgPC0gbGFiY2hhcnRfdGlkeSAlPiUgDQogIGRwbHlyOjpmaWx0ZXIoIShpZCAlaW4lIHJlbW92ZV90cmlhbF9lcnJvcikpDQpgYGANCg0KDQoNCiMjIEZpbHRlcmluZyBNTzINCg0KSGVyZSB3ZSBhcHBseSB0aGUgZm9sbG93aW5nIGZpbHRlcnMgdG8gdGhlIE1PMiBkYXRhOiA8YnI+DQoNCi0gUmVtb3ZlIHRoZSBmaXJzdCA1IFNNUiBjeWNsZXMgKGJ1cm4gaW4pDQotICo/UmVtb3ZlIGFsbCBwb3NpdGl2ZSBNTzIgKGRhdGEgaGFzIG5vdCBiZWVuIHN1YmplY3QgdG8gYW55IHRyYW5zZm9ybWF0aW9ucyB5ZXQpKiA7IHNob3VsZCB3ZSBkbyB0aGlzPw0KLSBSZW1vdmUgYWxsIE1PMiBjYWxjdWxhdGVkIHVzaW5nIGxlc3MgdGhlbiA2MCBkYXRhIHBvaW50cyAoNSBtaW4pIA0KLSBSZW1vdmUgYWxsIE1PMiBjYWxjdWxhdGVkIHdoZW4gcGhhc2UgNTAgY2xvc2UgKDUwYykgaGFkIGhpZ2ggTzIgKG8yIDwgNikgDQoNCg0KYGBge3J9DQpjeWNsZV9idXJuIDwtIDA6NA0KDQpsYWJjaGFydF90aWR5X2Zpc2ggPC0gbGFiY2hhcnRfdGlkeSAlPiUNCiAgZHBseXI6OmZpbHRlcighKGN5Y2xlICVpbiUgY3ljbGVfYnVybikgJiANCiAgICAgICAgICAgICAgICAgIG1vMmNvcnIgPCAwICYgDQogICAgICAgICAgICAgICAgICBuID4gNjAgJg0KICAgICAgICAgICAgICAgICAgY2hhbWJlcl9jb25kaXRpb24gPT0gImZpc2giDQogICAgICAgICAgICAgICAgKQ0KICANCiMgNTBjIHJlbW92ZSBjYXNlIHdpdGggaGlnaCBvMg0KbGFiY2hhcnRfdGlkeV9maXNoIDwtIGxhYmNoYXJ0X3RpZHlfZmlzaCAlPiUgDQogICAgZHBseXI6OmZpbHRlcighKHBoYXNlID09ICI1MGMiICYgDQogICAgICAgICAgICAgICAgICAgICAgbzIgPiA2KSkgIyBSZW1vdmluZyBhbnkgcGVyaW9kIGluIDUwYyB3aGVyZSBvMiB3YXMgdG8gaGlnaCAob3BlbmVkKQ0KYGBgDQoNCiMjIFNNUg0KDQpIZXJlIHdlIHdpbGwgZXN0aW1hdGUgU01SIHVzaW5nIHRoZSBtZWFuIG9mIHRoZSBsb3dlc3QgMyB2YXVsZXMNCg0KYGBge3J9DQpzbXJfM2xfbWVhbnMgPC0gbGFiY2hhcnRfdGlkeV9maXNoICU+JQ0KICBkcGx5cjo6Z3JvdXBfYnkoaWQpICU+JSANCiAgZHBseXI6OmZpbHRlcihwaGFzZSA9PSAic21yIikgJT4lDQogIGRwbHlyOjphcnJhbmdlKGRlc2MobW8yY29ycikpICU+JQ0KICBkcGx5cjo6c2xpY2VfaGVhZChuID0gMykgICU+JSAjIFNlbGVjdCB0aGUgdGhyZWUgbG93ZXN0IE1PMg0KICBkcGx5cjo6dW5ncm91cCgpICU+JSANCiAgZHBseXI6Omdyb3VwX2J5KGlkKSAlPiUgDQogIGRwbHlyOjpyZWZyYW1lKHNtcl9sMyA9IG1lYW4obW8yY29ycikpDQoNCiMgQ29tYmluZSB0aGUgcHJvY2Vzc2VkICJzbXIiIHBoYXNlIHdpdGggYWxsIG90aGVyIHBoYXNlcw0KbGFiY2hhcnRfdGlkeV9maXNoIDwtIGxhYmNoYXJ0X3RpZHlfZmlzaCAlPiUNCiAgZHBseXI6OmxlZnRfam9pbiguLCBzbXJfM2xfbWVhbnMsIGJ5ID0gImlkIikNCmBgYA0KDQo8YnI+DQpIZXJlIEkgYW0gdXNpbmcgdGhlIGNhbGNTTVIgZnVuY3Rpb24gdG8gZXN0aW1hdGUgU01SLiBXZSB1c2UgbWVhbiBvZiB0aGUgbG93ZXN0IG5vcm1hbCBkaXN0cmlidXRpb24gKE1MTkQpIHdoZXJlIENWbWxuZCA8IDUuNCBhbmQgdGhlIG1lYW4gb2YgdGhlIGxvd2VyIDIwJSBxdWFudGlsZSAocTAuMikgd2VyZSBDVm1sbmQgPiA1LjQsIGFzIGRlc2NyaWJlZCBpbiBDaGFib3QgRCwgU3RlZmZlbnNlbiBKRiwgRmFycmVsbCBBUCAoMjAxNikgaHR0cHM6Ly9kb2kub3JnLzEwLjExMTEvamZiLjEyODQ1LiBJZiBDVm1sbmQgaXMgbm90IGNhbGN1bGF0ZWQgd2UgdXNlZCBxMC4yLiANCg0KYGBge3IsIGVjaG89RkFMU0V9DQpsYWJjaGFydF9jaGFib3Rfc21yIDwtIGxhYmNoYXJ0X3RpZHlfZmlzaCAlPiUNCiAgZHBseXI6OmZpbHRlcihwaGFzZSA9PSAic21yIikNCg0KIyBFeHRyYWN0IGRpc3RpbmN0IElEcw0KaWRzIDwtIGxhYmNoYXJ0X2NoYWJvdF9zbXIgJT4lIA0KICBkcGx5cjo6ZGlzdGluY3QoaWQpICU+JSANCiAgZHBseXI6OnB1bGwoKQ0KDQojIEluaXRpYWxpc2UgYW4gZW1wdHkgbGlzdCB0byBzdG9yZSBTTVIgZGF0YQ0Kc21yX2xpc3QgPC0gbGlzdCgpDQoNCiMgUHJvY2VzcyBlYWNoIElEDQpmb3IgKGlkX2kgaW4gaWRzKSB7DQogIHRyeUNhdGNoKHsNCiAgICAjIEZpbHRlciB0aGUgZGF0YSBmb3IgdGhlIGN1cnJlbnQgSUQNCiAgICBkZl9pIDwtIGxhYmNoYXJ0X2NoYWJvdF9zbXIgJT4lIA0KICAgICAgZHBseXI6OmZpbHRlcihpZCA9PSBpZF9pKSAlPiUgDQogICAgICBkcGx5cjo6bXV0YXRlKGFic19tbzJjb3JyID0gYWJzKG1vMmNvcnIpKQ0KICAgIA0KICAgICMgQ2FsY3VsYXRlIFNNUiByZXN1bHRzDQogICAgY2FsY1NNUl9yZXN1bHRzIDwtIGNhbGNTTVIoZGZfaSRhYnNfbW8yY29ycikNCiAgICBDVm1sbmRfaSA8LSBjYWxjU01SX3Jlc3VsdHMkQ1ZtbG5kDQogICAgcXVhbnRfaSA8LSBjYWxjU01SX3Jlc3VsdHMkcXVhbnQgJT4lIGFzX3RpYmJsZSgpDQogICAgcXVhbnRfMjBwZXJfaSA8LSBxdWFudF9pJHZhbHVlWzNdDQogICAgbWxuZF9pIDwtIGNhbGNTTVJfcmVzdWx0cyRtbG5kDQogICAgc21yX3ZhbHVlIDwtIGlmX2Vsc2UoQ1ZtbG5kX2kgPCA1LjQsIG1sbmRfaSwgcXVhbnRfMjBwZXJfaSkNCiAgICBzbXJfdHlwZSA8LSBpZl9lbHNlKENWbWxuZF9pIDwgNS40LCAibWxuZCIsICJxdWFudF8yMHBlciIpDQogICAgc21yX3ZhbHVlIDwtIGlmX2Vsc2UoaXMubmEoc21yX3ZhbHVlKSwgcXVhbnRfMjBwZXJfaSwgc21yX3ZhbHVlKQ0KICAgIHNtcl90eXBlIDwtIGlmX2Vsc2UoaXMubmEoc21yX3R5cGUpLCAicXVhbnRfMjBwZXIiLCBzbXJfdHlwZSkNCiAgICANCiAgICAjIENyZWF0ZSBhIGRhdGEgZnJhbWUgZm9yIHRoZSBjdXJyZW50IElEDQogICAgc21yX2RmIDwtIHRpYmJsZSgNCiAgICAgIGlkID0gaWRfaSwNCiAgICAgIHNtciA9IHNtcl92YWx1ZSwNCiAgICAgIHNtcl9lc3QgPSBzbXJfdHlwZQ0KICAgICkNCiAgICANCiAgfSwgZXJyb3IgPSBmdW5jdGlvbihlKSB7DQogICAgIyBIYW5kbGUgZXJyb3JzIGJ5IGFzc2lnbmluZyBOQSB2YWx1ZXMNCiAgICBzbXJfZGYgPC0gdGliYmxlKA0KICAgICAgaWQgPSBpZF9pLA0KICAgICAgc21yID0gTkEsDQogICAgICBzbXJfZXN0ID0gTkENCiAgICApDQogIH0pDQogIA0KICAjIEFwcGVuZCB0byB0aGUgbGlzdA0KICBzbXJfbGlzdFtbaWRfaV1dIDwtIHNtcl9kZg0KfQ0KDQojIENvbWJpbmUgYWxsIGluZGl2aWR1YWwgU01SIGRhdGEgZnJhbWVzIGludG8gb25lDQpzbXJfZGYgPC0gYmluZF9yb3dzKHNtcl9saXN0KSAlPiUgDQogIGRwbHlyOjpyZW5hbWUoc21yX2NoYWJvdCA9IHNtciwNCiAgICAgICAgICAgICAgICBzbXJfY2hhYm90X21ldGhvZCA9IHNtcl9lc3QpDQoNCmxhYmNoYXJ0X3RpZHlfZmlzaCA8LSBsYWJjaGFydF90aWR5X2Zpc2ggJT4lDQogIGRwbHlyOjpsZWZ0X2pvaW4oLiwgc21yX2RmLCBieSA9ICJpZCIpDQpgYGANCg0KDQojIyBUcmFuc2Zvcm1pbmcgTU8yIHZhdWxlcw0KDQpIZXJlIHdlIGFyZSB0cmFuc2Zvcm1pbmcgdGhlIE1PMiB2YXVsZXMgDQoNCi0gTU8yID0gYWJzb2x1dGUgdmFsdWUgb2YgdGhlIGJhY2tncm91bmQgYW5kIGxlYWsgY29ycmVjdGVkIG1vMiBzbG9wZSBmcm9tIGxhYmNoYXJ0IChtbzJjb3JyKQ0KLSBNTzJfZyA9IE1PMiBkaXZpZGVkIGJ5IGZpc2ggbWFzcw0KLSBTTVIgPSBhYnNvbHV0ZSB2YWx1ZSBvZiB0aGUgbWVhbiBvZiB0aGUgdGhyZWUgbG93ZXN0IE1PMiBkdXJpbmcgdGhlIFNNUiBwaGFzZSAoc21yX2wzKQ0KLSBTTVJfZyA9IFNNUiBkaXZpZGVkIGJ5IGZpc2ggbWFzcw0KLSBTTVJfQ0hBQk9UID0gYWJzb2x1dGUgdmFsdWUgb2YgdGhlIFNNUiBlc3RpbWF0ZXMgdXNpbmcgQ2hhYm90IGV0IGFsIHJlY29tbWVuZGF0aW9ucyAoc21yX2NoYWJvdCkNCi0gU01SX2cgPSBTTVJfQ0hBQk9UIGRpdmlkZWQgYnkgZmlzaCBtYXNzDQotIERPID0gZGlzc29sdmVkIG94eWdlbiBwZXJjZW50YWdlIGNhbGN1bGF0ZWQgZnJvbSBvMiB2YWx1ZXMgKG1nL0wpIHVzaW5nIHRoZSByZWNvcmRlZCB0ZW1wZXJhdHVyZSwgc2FsaW5pdHksIGFuZCBhIGNvbnN0YW50IGF0bW9zcGhlcmljIHByZXNzdXJlICgxMDEzLjI1KQ0KDQoqTk9URSogOiBuZWVkIHRvIGNoYXQgYWJvdXQgdW5pdHMgYW5kIGNvcnJlY3Rpb25zLiBJIGtub3cgdGhlcmUgYXJlIHNvbWUgcGFydHMgYmVsb3cgdGhhdCBhcmUgaW5jb3JyZWN0LiANCg0KYGBge3J9DQojIENvbWJpbmUgYmFjayBpbnRvIG9uZSBkYXRhIGZyYW1lDQpsYWJjaGFydF90aWR5X2Zpc2ggPC0gbGFiY2hhcnRfdGlkeV9maXNoICU+JSANCiAgICBkcGx5cjo6bXV0YXRlKE1PMiA9IGFicyhtbzJjb3JyKSwNCiAgICAgICAgICAgICAgICAgIE1PMl9nID0gTU8yL21hc3MsDQogICAgICAgICAgICAgICAgICBTTVIgPSBhYnMoc21yX2wzKSwNCiAgICAgICAgICAgICAgICAgIFNNUl9nID0gU01SL21hc3MsDQogICAgICAgICAgICAgICAgICBTTVJfQ0hBQk9UID0gYWJzKHNtcl9jaGFib3QpLA0KICAgICAgICAgICAgICAgICAgU01SX0NIQUJPVF9nID0gU01SX0NIQUJPVC9tYXNzLA0KICAgICAgICAgICAgICAgICAgRE8gPSBjb252X28yKA0KICAgICAgICAgICAgICAgICAgbzIgPSBvMiwNCiAgICAgICAgICAgICAgICAgIGZyb20gPSAibWdfcGVyX2wiLA0KICAgICAgICAgICAgICAgICAgdG8gPSAicGVyY2VudF9hLnMuIiwNCiAgICAgICAgICAgICAgICAgIHRlbXAgPSB0ZW1wLCAjQw0KICAgICAgICAgICAgICAgICAgc2FsID0gbWVhc3VyZWRfc2FsaW5pdHksDQogICAgICAgICAgICAgICAgICBhdG1fcHJlcyA9IDEwMTMuMjUpLA0KICAgICAgICAgICAgICAgICAgbmV0X3ZvbHVtZSA9IHZvbHVtZSAtIG1hc3MsICMgRm9sbG93aW5nIGluc3RydWN0aW9ucyBmcm9tIEx1aXMNCiAgICAgICAgICAgICAgICAgIE1PMl9CRyA9IGFicyhtbzIqbmV0X3ZvbHVtZSo2MCo2MCksICMgRm9sbG93aW5nIGluc3RydWN0aW9ucyBmcm9tIEx1aXMgDQogICAgICAgICAgICAgICAgICBCRyA9IGJncm91bmQqdm9sdW1lKjYwKjYwLCAjIEZvbGxvd2luZyBpbnN0cnVjdGlvbnMgZnJvbSBMdWlzICh3b3VsZCBuZWVkIHRvIGFkZCBsZWFrIGRhdGEpDQogICAgICAgICAgICAgICAgICBNTzJfYWRqID0gTU8yX0JHICsgQkcsICMgRm9sbG93aW5nIGluc3RydWN0aW9ucyBmcm9tIEx1aXMgDQogICAgKQ0KYGBgDQoNCg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMgVmlzdWFsaXNlIA0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCg0KSGVyZSB3ZSBwbG90IGFsbCBNTzIgZGF0YS4gVGhpcyBpcyB0aGUgYWJzb2x1dGUgTU8yLCBjb3JyZWN0ZWQgZm9yIGJhY2tncm91bmQgcmVzcGlyYXRpb24gYW5kIGFueSBsZWFraW5nIHRoYXQgb2NjdXJyZWQgZG93biBhdCBsb3cgb3h5Z2VuIGxldmVscy4NCg0KYGBge3J9DQpsYWJjaGFydF90aWR5X2Zpc2ggJT4lIA0KICBkcGx5cjo6ZmlsdGVyKGNoYW1iZXJfY29uZGl0aW9uID09ICJmaXNoIikgJT4lIA0KICAgIGdncGxvdChhZXMoeSA9IE1PMl9nLCB4ID0gbzIsIGNvbG91ciA9IGlkKSkgKyAjIERlZmF1bHQgYWVzdGhldGljcw0KICBnZW9tX3BvaW50KHNob3cubGVnZW5kID0gRkFMU0UpICsNCiAgZ2VvbV9zbW9vdGgoYWVzKGdyb3VwID0gaWQpLCBtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFLCBjb2xvdXIgPSBzY2FsZXM6OmFscGhhKCJibGFjayIsIDAuNSkpICsgIyBUcmFuc3BhcmVudCBibGFjayBsaW5lcw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IFRSVUUsIGNvbG91ciA9ICJyZWQiKSArICMgT3ZlcmFsbCBzbW9vdGggbGluZQ0KICBnZW9tX3Ntb290aChzZSA9IFRSVUUsIGNvbG91ciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArDQogIHRoZW1lX2NsZWFuKCkgKw0KICBsYWJzKA0KICAgIHN1YnRpdGxlID0gIkFsbCB2YWx1ZXMiLA0KICAgIHggPSAiTzIiLA0KICAgIHkgPSAiTU8yIChnKSINCiAgKQ0KYGBgDQo8YnI+DQpMb29raW5nIGF0IHRoZSBkaWZmZXJlbmNlIHJlc3BvbnNlcyBpbiB0aGUgdHdvIHNhbGluaXR5IGdyb3Vwcy4gSXQncyBhcHBlYXJzIG1vcmUgdmFyaWFibGUgaW4gZnJlc2h3YXRlci4NCg0KYGBge3J9DQpsYWJjaGFydF90aWR5X2Zpc2ggJT4lIA0KICBkcGx5cjo6ZmlsdGVyKGNoYW1iZXJfY29uZGl0aW9uID09ICJmaXNoIikgJT4lIA0KICAgIGdncGxvdChhZXMoeSA9IE1PMl9nLCB4ID0gbzIsIGNvbG91ciA9IGlkKSkgKyAjIERlZmF1bHQgYWVzdGhldGljcw0KICBnZW9tX3BvaW50KHNob3cubGVnZW5kID0gRkFMU0UpICsNCiAgZ2VvbV9zbW9vdGgoYWVzKGdyb3VwID0gaWQpLCBtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFLCBjb2xvdXIgPSBzY2FsZXM6OmFscGhhKCJibGFjayIsIDAuNSkpICsgIyBUcmFuc3BhcmVudCBibGFjayBsaW5lcw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IFRSVUUsIGNvbG91ciA9ICJyZWQiKSArICMgT3ZlcmFsbCBzbW9vdGggbGluZQ0KICBnZW9tX3Ntb290aChzZSA9IFRSVUUsIGNvbG91ciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArDQogIHRoZW1lX2NsZWFuKCkgKw0KICBmYWNldF93cmFwKH5zYWxpbml0eV9ncm91cCkgKw0KICBsYWJzKA0KICAgIHN1YnRpdGxlID0gIm1vMiB2cyBvMiBieSBzYWxpbml0eSB0cmVhdG1lbnQiLA0KICAgIHggPSAibzIiLA0KICAgIHkgPSAibW8yIChnKSINCiAgKQ0KYGBgDQoNCjxicj4NCkxvb2tpbmcgYXQgdGhlIGRpZmZlcmVuY2UgY2hhbWJlciB0eXBlcw0KDQpgYGB7cn0NCmxhYmNoYXJ0X3RpZHlfZmlzaCAlPiUgDQogIGRwbHlyOjpmaWx0ZXIoY2hhbWJlcl9jb25kaXRpb24gPT0gImZpc2giKSAlPiUgDQogICAgZ2dwbG90KGFlcyh5ID0gTU8yX2csIHggPSBvMiwgY29sb3VyID0gaWQpKSArICMgRGVmYXVsdCBhZXN0aGV0aWNzDQogIGdlb21fcG9pbnQoc2hvdy5sZWdlbmQgPSBGQUxTRSkgKw0KICBnZW9tX3Ntb290aChhZXMoZ3JvdXAgPSBpZCksIG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UsIGNvbG91ciA9IHNjYWxlczo6YWxwaGEoImJsYWNrIiwgMC41KSkgKyAjIFRyYW5zcGFyZW50IGJsYWNrIGxpbmVzDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gVFJVRSwgY29sb3VyID0gInJlZCIpICsgIyBPdmVyYWxsIHNtb290aCBsaW5lDQogIGdlb21fc21vb3RoKHNlID0gVFJVRSwgY29sb3VyID0gInJlZCIsIGxpbmV0eXBlID0gImRhc2hlZCIpICsNCiAgdGhlbWVfY2xlYW4oKSArDQogIGZhY2V0X3dyYXAofmNoYW1iZXJfdHlwZSkgKw0KICBsYWJzKA0KICAgIHN1YnRpdGxlID0gIm1vMiB2cyBvMiBieSBzYWxpbml0eSB0cmVhdG1lbnQiLA0KICAgIHggPSAibzIiLA0KICAgIHkgPSAibW8yIChnKSINCiAgKQ0KYGBgDQoNCjxicj4NClBsb3R0aW5nIE1PMiBlc3RpbWF0ZXMgZm9yIGVhY2ggZmlzaC4gVGhlIGRhc2hlZCByZWQgbGluZSBpcyBDaGFib3QgU01SIG1ldGhvZHMsIGFuZCB0aGUgc29saWQgbGluZSBpcyB0aGUgbWVhbiBvZiB0aGUgbG93ZXN0IDMgbWVhc3VyZXMgKGV4Y2x1ZGluZyB0aGUgZmlyc3QgNSBjeWNsZXMpIDxicj4NCg0KKk5PVEVTKiA6IDxicj4NCi0qVGhlcmUncyBzb21ldGhpbmcgd2lyZWQgZ29pbmcgb24gd2l0aCBhXzBfMjVub3ZfMiBpdCBzZWVtcyBsaWtlIG1hbnkgb2YgdGhlIHJhdyBNTzIgdmFsdWVzIGFyZSBwb3NpdGl2ZS4qDQotICpPZnRlbiB0aGVyZSBzZWVtcyB0byBiZSBhIGxvdyBNTzIgdmF1bGUgYXQgYWJvdXQgNSBtZy9MIE8yKg0KPGJyPg0KDQoNCmBgYHtyfQ0KIyBDcmVhdGUgb3V0cHV0IGRpcmVjdG9yeSBpZiBuZWVkZWQNCm91dHB1dF9maWdfc2xvcGVzX3dkIDwtIGZpbGUucGF0aChvdXRwdXRfZmlnX3dkLCAic2xvcGVzIikNCmlmICghZGlyLmV4aXN0cyhvdXRwdXRfZmlnX3Nsb3Blc193ZCkpIHsNCiAgZGlyLmNyZWF0ZShvdXRwdXRfZmlnX3Nsb3Blc193ZCkNCn0NCg0KaWRzIDwtIGxhYmNoYXJ0X3RpZHlfZmlzaCAlPiUgDQogIGRwbHlyOjpkaXN0aW5jdChpZCkgJT4lIA0KICBwdWxsKGlkKSAlPiUgDQogIGFzLmxpc3QoKQ0KDQpNTzJfcGxvdF9saXN0IDwtICBsaXN0KCkNCg0KIyAxKSBPcGVuIHRoZSBQREYgZGV2aWNlIG9uY2UNCnBkZigNCiAgZmlsZSAgID0gZmlsZS5wYXRoKG91dHB1dF9maWdfc2xvcGVzX3dkLCAiY29tYmluZWRfc2xvcGVzLnBkZiIpLCANCiAgd2lkdGggID0gOCwgDQogIGhlaWdodCA9IDYNCikNCg0KIyAyKSBMb29wIG92ZXIgSURzIGFuZCBjcmVhdGUgZWFjaCBwbG90DQpmb3IgKGlkX2kgaW4gaWRzKSB7DQogIA0KICBzbXJfY2hhYm90IDwtIGxhYmNoYXJ0X3RpZHlfZmlzaCAlPiUgDQogICAgZHBseXI6OmZpbHRlcihpZCA9PSBpZF9pKSAlPiUgDQogICAgZHBseXI6OnNsaWNlKDEpICU+JSANCiAgICBkcGx5cjo6cHVsbChTTVJfQ0hBQk9UKQ0KICANCiAgc21yX2wzIDwtIGxhYmNoYXJ0X3RpZHlfZmlzaCAlPiUgDQogICAgZHBseXI6OmZpbHRlcihpZCA9PSBpZF9pKSAlPiUgDQogICAgZHBseXI6OnNsaWNlKDEpICU+JSANCiAgICBkcGx5cjo6cHVsbChTTVIpDQogIA0KICBwbG90IDwtIGxhYmNoYXJ0X3RpZHlfZmlzaCAlPiUgDQogICAgZHBseXI6OmZpbHRlcihpZCA9PSBpZF9pKSAlPiUgDQogICAgZ2dwbG90KGFlcyh4ID0gbzIsIHkgPSBNTzIpKSArDQogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gc21yX2NoYWJvdCwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiZGFya3JlZCIpICsNCiAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBzbXJfbDMsIGNvbG9yID0gImRhcmtyZWQiKSArDQogICAgZ2VvbV9wb2ludChhZXMoY29sb3VyID0gcGhhc2UpKSArDQogICAgdGhlbWVfY2xlYW4oKSArDQogICAgbGFicygNCiAgICAgIHN1YnRpdGxlID0gcGFzdGUwKGlkX2ksICIgc2xvcGVzIiksDQogICAgICB4ID0gIk1lYW4gbzIgKG1nX3Blcl9sKSIsDQogICAgICB5ID0gImFicyhtbzIpIChtZ19wZXJfbCkiDQogICAgKQ0KICANCiAgIyBJbnN0ZWFkIG9mIHNhdmluZyBlYWNoIHBsb3Qgc2VwYXJhdGVseSwganVzdCBwcmludCBpdA0KICBwcmludChwbG90KQ0KICANCiAgTU8yX3Bsb3RfbGlzdFtbaWRfaV1dIDwtIHBsb3QNCn0NCg0KIyAzKSBDbG9zZSB0aGUgUERGIGRldmljZSAqYWZ0ZXIqIHRoZSBsb29wDQpkZXYub2ZmKCkNCg0KZm9yIChwIGluIE1PMl9wbG90X2xpc3QpIHsNCiAgcHJpbnQocCkNCn0NCmBgYA0KDQoNCiMjIyMjIyMjIyMjIyMjIyMjDQojIFBjcml0IChPMmNyaXQpDQojIyMjIyMjIyMjIyMjIyMjDQoNCiMjIENoYWJvdCBtZXRob2QNCg0KSGVyZSB3ZSB3aWxsIGNhbGN1bGF0ZSBQY3JpdCB1c2luZyBDaGFib3QgbWV0aG9kIGFuZCBmdW5jdGlvbiBjYWxjTzJjcml0LiBXZSBhcmUgdXNpbmcgb3VyIGVzdGltYXRlcyBmb3IgU01SIChtZWFuIG9mIGxvd2VzdCB0aHJlZSkNCg0KYGBge3J9DQppZHMgPC0gbGFiY2hhcnRfdGlkeV9maXNoICU+JSANCiAgZHBseXI6OmRpc3RpbmN0KGlkKSAlPiUgDQogIGRwbHlyOjpwdWxsKCkNCg0KcGNyaXRfbW9kZWxfZGZfbGlzdCA8LSBsaXN0KCkNCnBjcml0X21vZGVscyA8LSAgbGlzdCgpDQoNCmZvciAoaWRfaSBpbiBpZHMpIHsNCg0KZGZfaSA8LSBsYWJjaGFydF90aWR5X2Zpc2ggJT4lIA0KICBkcGx5cjo6ZmlsdGVyKGlkID09IGlkX2kpDQoNCm8yY3JpdCA8LSBjYWxjTzJjcml0KERhdGEgPSBkZl9pLCBTTVIgPSBkZl9pJFNNUlsxXSwgbG93ZXN0TU8yPU5BLCBnYXBMaW1pdCA9IDQsDQogICAgICAgICAgICAgICAgICAgICBtYXgubmIuTU8yLmZvci5yZWcgPSA3KQ0KDQp2YXVsZSA8LSBvMmNyaXQkbzJjcml0DQpuYl9tbzJfY29uZm9ybWluZyA8LSBvMmNyaXQkTmJfTU8yX2NvbmZvcm1pbmcNCnIyIDwtIG8yY3JpdCRyMg0KbWV0aG9kIDwtIG8yY3JpdCRNZXRob2QNCnAgPC0gbzJjcml0JFBbMV0NCg0KcGNyaXRfbW9kZWxfZGYgPC0gdGliYmxlKA0KICAgICAgaWQgPSBpZF9pLA0KICAgICAgcGNyaXRfdmF1bGUgPSB2YXVsZSwNCiAgICAgIHBjcml0X25iX21vMl9jb25mb3JtaW5nID0gbmJfbW8yX2NvbmZvcm1pbmcsDQogICAgICBwY3JpdF9yMiA9IHIyLA0KICAgICAgcGNyaXRfbWV0aG9kID0gbWV0aG9kLA0KICAgICAgcGNyaXRfcCA9IHANCiAgICApDQoNCnBjcml0X21vZGVsX2RmX2xpc3RbW2lkX2ldXSA8LSBwY3JpdF9tb2RlbF9kZg0KDQpwY3JpdF9tb2RlbHNbW2lkX2ldXSA8LSBvMmNyaXQNCg0KfQ0KDQpwY3JpdF9tb2RlbF9kZiA8LSBiaW5kX3Jvd3MocGNyaXRfbW9kZWxfZGZfbGlzdCkNCmBgYA0KDQoNCiMjIyBQbG90aW5nIG8yIGNyaXQNCg0KSGVyZSdzIHRoZSBwbG90cyBmb3IgdGhlIFBjcml0IGVzdGltYXRlcw0KDQpgYGB7cn0NCiMgQ3JlYXRlIG91dHB1dCBkaXJlY3RvcnkgaWYgbmVlZGVkDQpvdXRwdXRfZmlnX3Bjcml0X2NoYWJvdF93ZCA8LSBmaWxlLnBhdGgob3V0cHV0X2ZpZ193ZCwgIm1vZGVsX2NoYWJvdCIpDQppZiAoIWRpci5leGlzdHMob3V0cHV0X2ZpZ19wY3JpdF9jaGFib3Rfd2QpKSB7DQogIGRpci5jcmVhdGUob3V0cHV0X2ZpZ19wY3JpdF9jaGFib3Rfd2QpDQp9DQoNCmlkcyA8LSBsYWJjaGFydF90aWR5X2Zpc2ggJT4lIA0KICBkcGx5cjo6ZGlzdGluY3QoaWQpICU+JSANCiAgZHBseXI6OnB1bGwoKQ0KDQpwY3JpdF9jaGFib3RfbGlzdCA8LSBsaXN0KCkNCg0KIyBPcGVuIGEgc2luZ2xlIFBERiBkZXZpY2UNCnBkZihmaWxlID0gZmlsZS5wYXRoKG91dHB1dF9maWdfcGNyaXRfY2hhYm90X3dkLCAiY29tYmluZWRfY2hhYm90X3Bsb3RzLnBkZiIpLCANCiAgICB3aWR0aCA9IDgsIGhlaWdodCA9IDYpDQoNCmZvciAoaWRfaSBpbiBpZHMpIHsNCiAgDQogIHIyIDwtIHBjcml0X21vZGVsX2RmICU+JSANCiAgICBkcGx5cjo6ZmlsdGVyKGlkID09IGlkX2kpICU+JSANCiAgICBkcGx5cjo6bXV0YXRlKHBjcml0X3IyID0gcm91bmQocGNyaXRfcjIsIDMpKSAlPiUgDQogICAgZHBseXI6OnB1bGwocGNyaXRfcjIpDQogIA0KICAjIEdlbmVyYXRlIGFuZCByZW5kZXIgdGhlIHBsb3QNCiAgcGxvdE8yY3JpdChvMmNyaXRvYmogPSBwY3JpdF9tb2RlbHNbW2lkX2ldXSkNCiAgDQogICMgQWRkIGEgdGl0bGUNCiAgbXRleHQoDQogICAgdGV4dCA9IHBhc3RlMChpZF9pLCAiIChSMiA9ICIsIHIyLCAiKSIpLA0KICAgIHNpZGUgPSAzLCBsaW5lID0gMiwgYWRqID0gMCwNCiAgICBjb2wgPSAiYmx1ZSIsIGZvbnQgPSAyLCBjZXggPSAxLjINCiAgKQ0KfQ0KDQojIENsb3NlIHRoZSBQREYgZGV2aWNlICphZnRlciogdGhlIGxvb3ANCmRldi5vZmYoKQ0KYGBgDQo8YnI+DQpQcmludGluZyBpbiBodGxtIGRvY3VtZW50IA0KDQpgYGB7cn0NCmlkcyA8LSBsYWJjaGFydF90aWR5X2Zpc2ggJT4lIA0KICBkcGx5cjo6ZGlzdGluY3QoaWQpICU+JSANCiAgZHBseXI6OnB1bGwoKQ0KDQpmb3IgKGlkX2kgaW4gaWRzKSB7DQogIA0KICByMiA8LSBwY3JpdF9tb2RlbF9kZiAlPiUgDQogICAgZHBseXI6OmZpbHRlcihpZCA9PSBpZF9pKSAlPiUgDQogICAgZHBseXI6Om11dGF0ZShwY3JpdF9yMiA9IHJvdW5kKHBjcml0X3IyLCAzKSkgJT4lIA0KICAgIGRwbHlyOjpwdWxsKHBjcml0X3IyKQ0KICANCiAgIyBHZW5lcmF0ZSBhbmQgcmVuZGVyIHRoZSBwbG90DQogIHBsb3RPMmNyaXQobzJjcml0b2JqID0gcGNyaXRfbW9kZWxzW1tpZF9pXV0pDQogIA0KICAjIEFkZCBhIHRpdGxlDQogIG10ZXh0KA0KICAgIHRleHQgPSBwYXN0ZTAoaWRfaSwgIiAoUjIgPSAiLCByMiwgIikiKSwNCiAgICBzaWRlID0gMywgbGluZSA9IDIsIGFkaiA9IDAsDQogICAgY29sID0gImJsdWUiLCBmb250ID0gMiwgY2V4ID0gMS4yDQogICkNCn0NCmBgYA0KDQoNCiMjIGNhbGNfcGNyaXQoKQ0KDQpIZXJlIHVzaW5nIHRoZSAqMTAwIGNsb3NlZCB0cmlhbHMqIHdlIHdpbGwgZXN0aW1hdGUgUGNyaXQgKGNvbW1vbmx5IHVuZGVyc3Rvb2QgYXMgdGhlIHRocmVzaG9sZCBiZWxvdyB3aGljaCBveHlnZW4gY29uc3VtcHRpb24gcmF0ZSBjYW4gbm8gbG9uZ2VyIGJlIHN1c3RhaW5lZCksIGJhc2VkIG9uIHBhaXJlZCBQTzIgYW5kIE1PMiB2YWx1ZXMgd2l0aCBmaXZlIHBvcHVsYXIgdGVjaG5pcXVlcyBmb3IgUGNyaXQgY2FsY3VsYXRpb246IHRoZSB0cmFkaXRpb25hbCBicmVha3BvaW50IG1ldHJpYyAoYnJva2VuIHN0aWNrIHJlZ3Jlc3Npb24pLCB0aGUgbm9ubGluZWFyIHJlZ3Jlc3Npb24gbWV0cmljIChNYXJzaGFsbCBldCBhbC4gMjAxMyksIHRoZSBzdWItcHJlZGljdGlvbiBpbnRlcnZhbCBtZXRyaWMgKEJpcmsgZXQgYWwuIDIwMTkpLCB0aGUgYWxwaGEtYmFzZWQgUGNyaXQgbWV0aG9kIChTZWliZWwgZXQgYWwuIDIwMjEpLCBhbmQgdGhlIGxpbmVhciBsb3cgTzIgKExMTykgbWV0aG9kIChSZWVtZXllciAmIFJlZXMgMjAxOSkuICA8YnI+DQoNCmh0dHBzOi8vc2VhcmNoLnItcHJvamVjdC5vcmcvQ1JBTi9yZWZtYW5zL3Jlc3Bpcm9tZXRyeS9odG1sL2NhbGNfcGNyaXQuaHRtbCAgPGJyPg0KDQpNYXJzaGFsbCBldCBhbCAoMjAxMykgc3VnZ2VzdCBub25saW5lYXIgcmVncmVzc2lvbiAoTkxSKSAgPGJyPg0KDQpIZXJlJ3MgYSBmdW5jdGlvbiB0byBjYWxjdWxhdGUgUGNyaXQsIHdlIGFyZSB1c2luZyBhIGZ1bmN0aW9uIGNhbGxlZCBjYWxjX3Bjcml0IGZyb20gdGhlIHJlc3Bpcm1ldGVyeSBwYWNrYWdlLiAgPGJyPg0KDQojIyMgIXBhcmFtZXRlcnMhDQoNClBhcmFtZXRlcnMgdG8gY29uc2lkZXIgPGJyPg0KDQotICoqYXZnX3RvcF9uKio6IGZvciBhbHBoYSBtZXRob2QsIGEgbnVtZXJpYyB2YWx1ZSByZXByZXNlbnRpbmcgdGhlIG51bWJlciBvZiB0b3AgzrEwIChNTzIvUE8yKSB2YWx1ZXMgdG8gYXZlcmFnZSB0b2dldGhlciB0byBlc3RpbWF0ZSDOsS4gRGVmYXVsdCBpcyAxLiBXZSByZWNvbW1lbmQgbm8gbW9yZSB0aGFuIDMgdG8gYXZvaWQgZGltaW5pc2hpbmcgdGhlIM6xIHZhbHVlIHdpdGggc3ViLW1heGltYWwgb2JzZXJ2YXRpb25zLiA8YnI+DQoNCi0gKipsZXZlbCoqOiBmb3IgU3ViX1BJIG1ldGhvZCwgUGVyY2VudGFnZSBhdCB3aGljaCB0aGUgcHJlZGljdGlvbiBpbnRlcnZhbCBzaG91bGQgYmUgY29uc3RydWN0ZWQuIDxicj4NCg0KLSAqKmlxcioqOiBPbmx5IGZvciBTdWJfUEkuIFJlbW92ZXMgbW8yIG9ic2VydmF0aW9ucyB0aGF0IGFyZSB0aGlzIG1hbnkgaW50ZXJxdWFydGlsZSByYW5nZXMgYXdheSBmcm9tIHRoZSBtZWFuIHZhbHVlIGZvciB0aGUgb3h5cmVndWxhdGluZyBwb3J0aW9uIG9mIHRoZSB0cmlhbC4gSWYgdGhpcyBmaWx0ZXJpbmcgaXMgbm90IGRlc2lyZWQsIHNldCB0byBpbmZpbml0eS4gIDxicj4NCg0KLSAqKk5MUl9tKio6IG9ubHkgYXBwbGllcyB0byBOTFIuIFBjcml0IGlzIGRlZmluZWQgYXMgdGhlIFBPMiBhdCB3aGljaCB0aGUgc2xvcGUgb2YgdGhlIGJlc3QgZml0dGluZyBmdW5jdGlvbiBlcXVhbHMgTkxSX20gKGFmdGVyIHRoZSBNTzIgZGF0YSBhcmUgbm9ybWFsaXplZCB0byB0aGUgOTAlIHF1YW50aWxlKS4gRGVmYXVsdCBpcyAwLjA2NSA8YnI+DQoNCi0gKipNUioqOiBBIG51bWVyaWMgdmFsdWUgZm9yIHRoZSBtZXRhYm9saWMgcmF0ZSBhdCB3aGljaCBwY3JpdF9hbHBoYSBhbmQgcGNyaXRfTExPIHNob3VsZCBiZSByZXR1cm5lZC4gSWYgbm90IHN1cHBsaWVkIGJ5IHRoZSB1c2VyLCB0aGVuIHRoZSBtZWFuIE1PMiBvZiB0aGUgIm94eXJlZ3VsYXRpbmciIHBvcnRpb24gb2YgdGhlIGN1cnZlIGlzIGFwcGxpZWQgZm9yIHBjcml0X2FscGhhIGFuZCBOQSBpcyByZXR1cm5lZCBmb3IgcGNyaXRfTExPLiA8YnI+DQoNCi0gKiptbzJfdGhyZXNob2xkKio6IEEgc2luZ2xlIG51bWVyaWMgdmFsdWUgYWJvdmUgd2hpY2ggbW8yIHZhbHVlcyBhcmUgaWdub3JlZCBmb3IgYWxwaGEgUGNyaXQgZXN0aW1hdGlvbi4gVXNlZnVsIHRvIHJlbW92aW5nIG9idmlvdXNseSBlcnJvbmVvdXMgdmFsdWVzLiBEZWZhdWx0IGlzIEluZi4NCg0KIyMjIEZvcm1hdGUgZGF0YQ0KDQpXZSB3aWxsIG9ubHkgdXNlIDEwMCBjIHRyYWlscyBmb3IgdGhpcy4NCg0KYGBge3J9DQpsYWJjaGFydF90aWR5X2Zpc2hfMTAwYyA8LSBsYWJjaGFydF90aWR5X2Zpc2ggJT4lIA0KICBkcGx5cjo6ZmlsdGVyKHBoYXNlID09ICIxMDBjIikNCg0KbGFiY2hhcnRfdGlkeV9maXNoXzEwMGNfbiA8LSBsYWJjaGFydF90aWR5X2Zpc2hfMTAwYyAlPiUgDQogIGRwbHlyOjpkaXN0aW5jdChpZCkgJT4lIA0KICBucm93KC4pDQoNCnBhc3RlMCgibiBmb3IgMTAwIGNsb3NlZCA9ICIsIGxhYmNoYXJ0X3RpZHlfZmlzaF8xMDBjX24pDQpgYGANCiA8YnI+DQpGaXJzdCB3ZSB3aWxsIGJ1aWxkIGEgbW9kZWwgd2l0aCAzIFNNUiB2YWx1ZXMgYW5kIGFsbCB0aGUgNzVjIGFuZCA1MGMgKG9yIDEwMGMpIGRhdGENCg0KYGBge3J9DQpjb21iaW5lZF9wY2lydF9saXN0IDwtIGxpc3QoKQ0KDQppZHMgPC0gbGFiY2hhcnRfdGlkeV9maXNoXzEwMGMgJT4lIA0KICBkcGx5cjo6ZGlzdGluY3QoaWQpICU+JSANCiAgcHVsbChpZCkgJT4lIA0KICBhcy5saXN0KCkNCg0KDQpmb3IgKGlkX2kgaW4gaWRzKSB7DQoNCiAgaWRfbmFtZSA8LSBpZF9pDQogIA0KICBtbzJfZGF0YSA8LSBsYWJjaGFydF90aWR5X2Zpc2hfMTAwYyAlPiUgDQogICAgZHBseXI6OmZpbHRlcihpZCA9PSBpZF9pKQ0KICANCiAgTVJfc2V0IDwtIG1vMl9kYXRhJFNNUlsxXSAlPiUgYXMubnVtZXJpYygpDQogIA0KICAjIFVzZSB0cnlDYXRjaCB0byBoYW5kbGUgZXJyb3JzIGFuZCBza2lwIHByb2JsZW1hdGljIGNhbGN1bGF0aW9ucw0KICBwY3JpdF9kZiA8LSB0cnlDYXRjaCh7DQogICAgDQogICAgcGNyaXRfZGYgPC0gY2FsY19wY3JpdChwbzIgPSBtbzJfZGF0YSRvMiwgDQogICAgICAgICAgIG1vMiA9IG1vMl9kYXRhJE1PMiwgDQogICAgICAgICAgIG1ldGhvZCA9ICdBbGwnLA0KICAgICAgICAgICBhdmdfdG9wX24gPSAyLCAjIGFscGhhIG1ldHJpYyAoZGVmYXVsdCA9IDEpIHJlY29tbWVuZCBubyBtb3JlIHRoYW4gMw0KICAgICAgICAgICBsZXZlbCA9IDAuOTUsICMgU3ViX1BJIG1ldHJpYyAoZGVmYXVsdCA9IDAuOTUpDQogICAgICAgICAgIGlxciA9IDEuNSwgIyBTdWJfUEkgbWV0cmljIChkZWZhdWx0ID0gMS41KQ0KICAgICAgICAgICBOTFJfbSA9IDAuMDY1LCAjIE5MUiBtZXRyaWMgKGRlZmF1bHQgPSAwLjA2NSkNCiAgICAgICAgICAgTVIgPSBNUl9zZXQsICMgYWxwaGEgYW5kIExMTyBtZXRyaWNzLA0KICAgICAgICAgICBtbzJfdGhyZXNob2xkID0gSW5mLCAjIGFscGhhIG1ldHJpYw0KICAgICAgICAgICByZXR1cm5fbW9kZWxzID0gRkFMU0UgIyByZXR1cm4gbW9kZWwgcGFyYW1ldGVycz8NCiAgICAgICAgICAgKSAlPiUNCiAgICAgIGFzLmRhdGEuZnJhbWUoKSAlPiUNCiAgICAgIHJvd25hbWVzX2FzX2NvbHVtbih2YXIgPSAibWV0aG9kIikgJT4lDQogICAgICByZW5hbWUodmFsdWUgPSAiLiIpICU+JQ0KICAgICAgdGlkeXI6OnBpdm90X3dpZGVyKC4sDQogICAgICAgICAgICAgICAgICAgICBuYW1lc19mcm9tID0gbWV0aG9kLA0KICAgICAgICAgICAgICAgICAgICAgdmFsdWVzX2Zyb20gPSB2YWx1ZSkgJT4lDQogICAgICBkcGx5cjo6bXV0YXRlKGlkID0gaWRfbmFtZSkgJT4lDQogICAgICBkcGx5cjo6c2VsZWN0KGlkLCBldmVyeXRoaW5nKCkpDQogICAgDQogIH0sIGVycm9yID0gZnVuY3Rpb24oZSkgew0KICAgIG1lc3NhZ2UoIlNraXBwaW5nIGNoYW5uZWwgIiwgaWRfbmFtZSwgIiBkdWUgdG8gZXJyb3I6ICIsIGNvbmRpdGlvbk1lc3NhZ2UoZSkpDQogICAgTlVMTA0KICB9KQ0KICANCiAgIyBPbmx5IGFkZCB0byBsaXN0IGlmIHBjcml0X2RmIGlzIG5vdCBOVUxMDQogIGlmICghaXMubnVsbChwY3JpdF9kZikpIHsNCiAgICBjb21iaW5lZF9wY2lydF9saXN0W1tpZF9uYW1lXV0gPC0gcGNyaXRfZGYNCiAgfQ0KfQ0KYGBgDQoNCjxicj4NCkNvbWJpbmVkIGFsbCB0aGUgUGNyaXQgbW9kZWwgZXN0aW1hdGVzIHRvZ2V0aGVyIA0KDQpgYGB7cn0NCnBjaXJ0IDwtIGJpbmRfcm93cyhjb21iaW5lZF9wY2lydF9saXN0KQ0KYGBgDQoNCiMjIyBQbG90IFBjcml0DQoNCkhlcmUgd2Ugd2lsbCBwbG90IHRoZSB2YXJpb3VzIFBjcml0IGN1cnZlcyANCg0KYGBge3J9DQojIENyZWF0ZSBvdXRwdXQgZGlyZWN0b3J5IGlmIG5lZWRlZA0Kb3V0cHV0X2ZpZ19wY3JpdF8xMDBjX3dkIDwtIGZpbGUucGF0aChvdXRwdXRfZmlnX3dkLCAicGNyaXQtMTAwYyIpDQppZiAoIWRpci5leGlzdHMob3V0cHV0X2ZpZ19wY3JpdF8xMDBjX3dkKSkgew0KICBkaXIuY3JlYXRlKG91dHB1dF9maWdfcGNyaXRfMTAwY193ZCkNCn0NCg0KaWRzIDwtIGxhYmNoYXJ0X3RpZHlfZmlzaF8xMDBjICU+JSANCiAgZHBseXI6OmRpc3RpbmN0KGlkKSAlPiUgDQogIHB1bGwoaWQpICU+JSANCiAgYXMubGlzdCgpDQoNCiMgT3BlbiBhIHNpbmdsZSBQREYgZGV2aWNlIG9uY2UNCnBkZihmaWxlID0gZmlsZS5wYXRoKG91dHB1dF9maWdfcGNyaXRfMTAwY193ZCwgImNvbWJpbmVkX3Bjcml0X3Bsb3RzLnBkZiIpLCANCiAgICB3aWR0aCA9IDgsIGhlaWdodCA9IDYpDQoNCmZvciAoaWRfaSBpbiBpZHMpIHsNCiAgDQogIGlkX25hbWUgPC0gaWRfaQ0KICANCiAgbW8yX2RhdGEgPC0gbGFiY2hhcnRfdGlkeV9maXNoXzEwMGMgJT4lIA0KICAgIGRwbHlyOjpmaWx0ZXIoaWQgPT0gaWRfaSkNCiAgDQogIE1SX3NldCA8LSBtbzJfZGF0YSRTTVJbMV0gJT4lIGFzLm51bWVyaWMoKQ0KICANCiAgdHJ5Q2F0Y2goew0KICAgICMgR2VuZXJhdGUgYW5kIHJlbmRlciB0aGUgcGxvdA0KICAgIHBsb3RfcGNyaXQoDQogICAgICBwbzIgPSBtbzJfZGF0YSRvMiwgDQogICAgICBtbzIgPSBtbzJfZGF0YSRNTzIsIA0KICAgICAgbWV0aG9kID0gJ0FsbCcsDQogICAgICBhdmdfdG9wX24gPSAxLCANCiAgICAgIGxldmVsID0gMC45NSwgDQogICAgICBpcXIgPSAxLjUsIA0KICAgICAgTkxSX20gPSAwLjA2NSwgDQogICAgICBNUiA9IE1SX3NldCwgDQogICAgICBtbzJfdGhyZXNob2xkID0gSW5mLCANCiAgICAgIHJldHVybl9tb2RlbHMgPSBGQUxTRSwgDQogICAgICBzaG93TkxScyA9IEZBTFNFDQogICAgKQ0KICAgIA0KICAgICMgQWRkIGEgdGl0bGUgaW4gdGhlIHRvcC1sZWZ0IGNvcm5lcg0KICAgIG10ZXh0KHRleHQgPSBwYXN0ZShpZF9uYW1lKSwNCiAgICAgICAgICBzaWRlID0gMywgbGluZSA9IDIsIGFkaiA9IDAsICMgVG9wIG1hcmdpbiwgYWxpZ25lZCB0byBsZWZ0DQogICAgICAgICAgY29sID0gImJsdWUiLCBmb250ID0gMiwgY2V4ID0gMS4yKQ0KICAgIA0KICB9LCBlcnJvciA9IGZ1bmN0aW9uKGUpIHsNCiAgICBtZXNzYWdlKCJTa2lwcGluZyBjaGFubmVsICIsIGlkX25hbWUsICIgZHVlIHRvIGVycm9yOiAiLCBjb25kaXRpb25NZXNzYWdlKGUpKQ0KICB9KQ0KfQ0KDQojIENsb3NlIHRoZSBQREYgZGV2aWNlICphZnRlciogdGhlIGxvb3ANCmRldi5vZmYoKQ0KYGBgDQoNCjxicj4NClBsb3R0aW5nIGluIHRoZSBodG1sIA0KDQpgYGB7cn0NCmlkcyA8LSBsYWJjaGFydF90aWR5X2Zpc2hfMTAwYyAlPiUgDQogIGRwbHlyOjpkaXN0aW5jdChpZCkgJT4lIA0KICBwdWxsKGlkKSAlPiUgDQogIGFzLmxpc3QoKQ0KDQpmb3IgKGlkX2kgaW4gaWRzKSB7DQogIA0KICBpZF9uYW1lIDwtIGlkX2kNCiAgDQogIG1vMl9kYXRhIDwtIGxhYmNoYXJ0X3RpZHlfZmlzaF8xMDBjICU+JSANCiAgICBkcGx5cjo6ZmlsdGVyKGlkID09IGlkX2kpDQogIA0KICBNUl9zZXQgPC0gbW8yX2RhdGEkU01SWzFdICU+JSBhcy5udW1lcmljKCkNCiAgDQogIHRyeUNhdGNoKHsNCiAgICAjIEdlbmVyYXRlIGFuZCByZW5kZXIgdGhlIHBsb3QNCiAgICBwbG90X3Bjcml0KA0KICAgICAgcG8yID0gbW8yX2RhdGEkbzIsIA0KICAgICAgbW8yID0gbW8yX2RhdGEkTU8yLCANCiAgICAgIG1ldGhvZCA9ICdBbGwnLA0KICAgICAgYXZnX3RvcF9uID0gMSwgDQogICAgICBsZXZlbCA9IDAuOTUsIA0KICAgICAgaXFyID0gMS41LCANCiAgICAgIE5MUl9tID0gMC4wNjUsIA0KICAgICAgTVIgPSBNUl9zZXQsIA0KICAgICAgbW8yX3RocmVzaG9sZCA9IEluZiwgDQogICAgICByZXR1cm5fbW9kZWxzID0gRkFMU0UsIA0KICAgICAgc2hvd05MUnMgPSBGQUxTRQ0KICAgICkNCiAgICANCiAgICAjIEFkZCBhIHRpdGxlIGluIHRoZSB0b3AtbGVmdCBjb3JuZXINCiAgICBtdGV4dCh0ZXh0ID0gcGFzdGUoaWRfbmFtZSksDQogICAgICAgICAgc2lkZSA9IDMsIGxpbmUgPSAyLCBhZGogPSAwLCAjIFRvcCBtYXJnaW4sIGFsaWduZWQgdG8gbGVmdA0KICAgICAgICAgIGNvbCA9ICJibHVlIiwgZm9udCA9IDIsIGNleCA9IDEuMikNCiAgICANCiAgfSwgZXJyb3IgPSBmdW5jdGlvbihlKSB7DQogICAgbWVzc2FnZSgiU2tpcHBpbmcgY2hhbm5lbCAiLCBpZF9uYW1lLCAiIGR1ZSB0byBlcnJvcjogIiwgY29uZGl0aW9uTWVzc2FnZShlKSkNCiAgfSkNCn0NCmBgYA0KDQo=